From 45b716e162b55cd9c3d76d285ed1632d2ff3050c Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Mon, 5 Oct 2015 14:40:15 +0100 Subject: [PATCH 01/35] Bug 1210707 - Feedback view no longer allows closing the window and avoiding leaving feedback. r=mikedeboer --- .../loop/content/js/conversationAppStore.js | 3 ++- .../test/desktop-local/conversationAppStore_test.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/browser/components/loop/content/js/conversationAppStore.js b/browser/components/loop/content/js/conversationAppStore.js index e6a40d147d5c..bc37f2addec5 100644 --- a/browser/components/loop/content/js/conversationAppStore.js +++ b/browser/components/loop/content/js/conversationAppStore.js @@ -147,7 +147,8 @@ loop.store.ConversationAppStore = (function() { this._dispatcher.dispatch(new loop.shared.actions.HangupCall()); break; case "room": - if (this._activeRoomStore.getStoreState().used) { + if (this._activeRoomStore.getStoreState().used && + !this._storeState.showFeedbackForm) { this._dispatcher.dispatch(new loop.shared.actions.LeaveRoom()); } else { loop.shared.mixins.WindowCloseMixin.closeWindow(); diff --git a/browser/components/loop/test/desktop-local/conversationAppStore_test.js b/browser/components/loop/test/desktop-local/conversationAppStore_test.js index 5ed80b061eca..1ad14e01be1f 100644 --- a/browser/components/loop/test/desktop-local/conversationAppStore_test.js +++ b/browser/components/loop/test/desktop-local/conversationAppStore_test.js @@ -250,6 +250,19 @@ describe("loop.store.ConversationAppStore", function () { sinon.assert.notCalled(loop.shared.mixins.WindowCloseMixin.closeWindow); }); + it("should close the window when a room was used and it showed feedback", function() { + store.setStoreState({ + showFeedbackForm: true, + windowType: "room" + }); + roomUsed = true; + + store.LoopHangupNowHandler(); + + sinon.assert.notCalled(dispatcher.dispatch); + sinon.assert.calledOnce(loop.shared.mixins.WindowCloseMixin.closeWindow); + }); + it("should close the window when a room was not used", function() { store.setStoreState({ windowType: "room" }); From 8de4febb074cd58db922feac3481669a1414e280 Mon Sep 17 00:00:00 2001 From: Manuel Casas Date: Mon, 5 Oct 2015 14:40:15 +0100 Subject: [PATCH 02/35] Bug 1172662 - ICE failures occuring in Loop conversations should be reported to the user. r=Standard8 --- .../loop/content/js/conversationViews.js | 2 + .../loop/content/js/conversationViews.jsx | 2 + .../components/loop/content/js/roomViews.js | 11 ++++- .../components/loop/content/js/roomViews.jsx | 11 ++++- .../loop/content/shared/js/otSdkDriver.js | 7 +++ .../loop/content/shared/js/utils.js | 3 +- .../content/js/standaloneRoomViews.js | 2 + .../content/js/standaloneRoomViews.jsx | 2 + .../content/l10n/en-US/loop.properties | 1 + .../desktop-local/conversationViews_test.js | 10 +++++ .../loop/test/desktop-local/roomViews_test.js | 12 ++++- .../loop/test/shared/otSdkDriver_test.js | 45 +++++++++++++++++++ .../standalone/standaloneRoomViews_test.js | 11 +++++ .../en-US/chrome/browser/loop/loop.properties | 1 + 14 files changed, 116 insertions(+), 4 deletions(-) diff --git a/browser/components/loop/content/js/conversationViews.js b/browser/components/loop/content/js/conversationViews.js index 6c1245dc194f..e5b6d92f2c4d 100644 --- a/browser/components/loop/content/js/conversationViews.js +++ b/browser/components/loop/content/js/conversationViews.js @@ -404,6 +404,8 @@ loop.conversationViews = (function(mozL10n) { case FAILURE_DETAILS.TOS_FAILURE: return mozL10n.get("tos_failure_message", { clientShortname: mozL10n.get("clientShortname2") }); + case FAILURE_DETAILS.ICE_FAILED: + return mozL10n.get("ice_failure_message"); default: return mozL10n.get("generic_failure_message"); } diff --git a/browser/components/loop/content/js/conversationViews.jsx b/browser/components/loop/content/js/conversationViews.jsx index 642c1282670d..262218453f69 100644 --- a/browser/components/loop/content/js/conversationViews.jsx +++ b/browser/components/loop/content/js/conversationViews.jsx @@ -404,6 +404,8 @@ loop.conversationViews = (function(mozL10n) { case FAILURE_DETAILS.TOS_FAILURE: return mozL10n.get("tos_failure_message", { clientShortname: mozL10n.get("clientShortname2") }); + case FAILURE_DETAILS.ICE_FAILED: + return mozL10n.get("ice_failure_message"); default: return mozL10n.get("generic_failure_message"); } diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index a14a18a9a7ad..31c2f8215077 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -8,6 +8,7 @@ loop.roomViews = (function(mozL10n) { var ROOM_STATES = loop.store.ROOM_STATES; var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; + var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS; var sharedActions = loop.shared.actions; var sharedMixins = loop.shared.mixins; var sharedUtils = loop.shared.utils; @@ -99,6 +100,14 @@ loop.roomViews = (function(mozL10n) { { id: "feedback" }, { id: "help" } ]; + + var btnTitle; + if (this.props.failureReason === FAILURE_DETAILS.ICE_FAILED) { + btnTitle = mozL10n.get("retry_call_button"); + } else { + btnTitle = mozL10n.get("rejoin_button"); + } + return ( React.createElement("div", {className: "room-failure"}, React.createElement(loop.conversationViews.FailureInfoView, { @@ -106,7 +115,7 @@ loop.roomViews = (function(mozL10n) { React.createElement("div", {className: "btn-group call-action-group"}, React.createElement("button", {className: "btn btn-info btn-rejoin", onClick: this.handleRejoinCall}, - mozL10n.get("rejoin_button") + btnTitle ) ), React.createElement(loop.shared.views.SettingsControlButton, { diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index 49c54a07dd55..627412fb316c 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -8,6 +8,7 @@ loop.roomViews = (function(mozL10n) { var ROOM_STATES = loop.store.ROOM_STATES; var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; + var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS; var sharedActions = loop.shared.actions; var sharedMixins = loop.shared.mixins; var sharedUtils = loop.shared.utils; @@ -99,6 +100,14 @@ loop.roomViews = (function(mozL10n) { { id: "feedback" }, { id: "help" } ]; + + var btnTitle; + if (this.props.failureReason === FAILURE_DETAILS.ICE_FAILED) { + btnTitle = mozL10n.get("retry_call_button"); + } else { + btnTitle = mozL10n.get("rejoin_button"); + } + return (
Date: Mon, 5 Oct 2015 11:59:01 +0200 Subject: [PATCH 03/35] Bug 1197147 - Gradle build: Use version 8.1 of Google Play Services. r=nalexander --HG-- extra : commitid : Cugw33Fj8QG extra : rebase_source : 404811c62a45b089095af8e4b4e952fdb183bb78 --- mobile/android/gradle/base/build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mobile/android/gradle/base/build.gradle b/mobile/android/gradle/base/build.gradle index 73681bd86c09..aa9803412957 100644 --- a/mobile/android/gradle/base/build.gradle +++ b/mobile/android/gradle/base/build.gradle @@ -76,8 +76,9 @@ dependencies { if (mozconfig.substs.MOZ_NATIVE_DEVICES) { compile 'com.android.support:mediarouter-v7:22.2.1' - compile 'com.google.android.gms:play-services-base:7.8.0' - compile 'com.google.android.gms:play-services-cast:7.8.0' + compile 'com.google.android.gms:play-services-basement:8.1.0' + compile 'com.google.android.gms:play-services-base:8.1.0' + compile 'com.google.android.gms:play-services-cast:8.1.0' } compile project(':branding') From faf41dd8da5d2c841d93f250218b1e140e9033b8 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Mon, 5 Oct 2015 14:59:53 +0200 Subject: [PATCH 04/35] Bug 1197147 - Mach build: Use version 8.1 of Google Play Services. r=nalexander This patch also adds the new base (sic) library play-services-basement. Note that the package names have changed too: * play-services-base: com.google.gms -> com.google.gms.base * play-services-basement: * -> com.google.gms --HG-- extra : commitid : EcmxZA10rzV extra : rebase_source : f39b361807a0b8227f3fb9a6d73e066241c8e36c --- build/autoconf/android.m4 | 5 +++-- mobile/android/base/Makefile.in | 3 +++ mobile/android/base/moz.build | 8 +++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index c046b8846c88..cb4eb2dbf415 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -295,8 +295,9 @@ AC_DEFUN([MOZ_ANDROID_GOOGLE_PLAY_SERVICES], if test -n "$MOZ_NATIVE_DEVICES" ; then AC_SUBST(MOZ_NATIVE_DEVICES) - MOZ_ANDROID_AAR(play-services-base, 7.8.0, google, com/google/android/gms) - MOZ_ANDROID_AAR(play-services-cast, 7.8.0, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-base, 8.1.0, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-basement, 8.1.0, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-cast, 8.1.0, google, com/google/android/gms) MOZ_ANDROID_AAR(mediarouter-v7, 22.2.1, android, com/android/support, REQUIRED_INTERNAL_IMPL) fi diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index e19d5cbeaf5e..0ee6c11e1de1 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -66,6 +66,7 @@ JAVA_CLASSPATH += \ ifdef MOZ_NATIVE_DEVICES JAVA_CLASSPATH += \ $(ANDROID_PLAY_SERVICES_BASE_AAR_LIB) \ + $(ANDROID_PLAY_SERVICES_BASEMENT_AAR_LIB) \ $(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \ $(ANDROID_MEDIAROUTER_V7_AAR_LIB) \ $(ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB) \ @@ -86,6 +87,7 @@ java_bundled_libs := \ ifdef MOZ_NATIVE_DEVICES java_bundled_libs += \ $(ANDROID_PLAY_SERVICES_BASE_AAR_LIB) \ + $(ANDROID_PLAY_SERVICES_BASEMENT_AAR_LIB) \ $(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \ $(ANDROID_MEDIAROUTER_V7_AAR_LIB) \ $(ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB) \ @@ -372,6 +374,7 @@ generated/android/support/v7/appcompat/R.java: .aapt.deps ; generated/android/support/v7/mediarouter/R.java: .aapt.deps ; generated/android/support/v7/recyclerview/R.java: .aapt.deps ; generated/com/google/android/gms/R.java: .aapt.deps ; +generated/com/google/android/gms/base/R.java: .aapt.deps ; generated/com/google/android/gms/cast/R.java: .aapt.deps ; gecko.ap_: .aapt.deps ; diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index a11fd81f3bab..8a09891e005b 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -654,6 +654,7 @@ moz_native_devices_jars = [ CONFIG['ANDROID_MEDIAROUTER_V7_AAR_LIB'], CONFIG['ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB'], CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR_LIB'], + CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR_LIB'], CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR_LIB'], ] moz_native_devices_sources = [ @@ -672,8 +673,13 @@ if CONFIG['MOZ_NATIVE_DEVICES']: resjar.generated_sources += ['android/support/v7/mediarouter/R.java'] if CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR']: - ANDROID_EXTRA_PACKAGES += ['com.google.android.gms'] + ANDROID_EXTRA_PACKAGES += ['com.google.android.gms.base'] ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR_RES']] + resjar.generated_sources += ['com/google/android/gms/base/R.java'] + + if CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR']: + ANDROID_EXTRA_PACKAGES += ['com.google.android.gms'] + ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR_RES']] resjar.generated_sources += ['com/google/android/gms/R.java'] if CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR']: From 52ef7844e3b00142a7dcc8049721eb4d6f2c1120 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Mon, 5 Oct 2015 15:00:21 +0200 Subject: [PATCH 05/35] Bug 1210755 - Gradle build: Compile with SDK version 23 and use build tools 23.0.1. r=nalexander --HG-- extra : commitid : 9z7e5mbgX8m extra : rebase_source : f8c2162458d68ab83d639a0b0b2fc758f627822a --- mobile/android/gradle/app/build.gradle | 4 ++-- mobile/android/gradle/base/build.gradle | 4 ++-- mobile/android/gradle/branding/build.gradle | 4 ++-- mobile/android/gradle/preprocessed_code/build.gradle | 4 ++-- mobile/android/gradle/preprocessed_resources/build.gradle | 4 ++-- mobile/android/gradle/thirdparty/build.gradle | 4 ++-- mobile/android/gradle/thirdparty_adjust_sdk/build.gradle | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mobile/android/gradle/app/build.gradle b/mobile/android/gradle/app/build.gradle index 3703438e22cc..a43c76019df9 100644 --- a/mobile/android/gradle/app/build.gradle +++ b/mobile/android/gradle/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 diff --git a/mobile/android/gradle/base/build.gradle b/mobile/android/gradle/base/build.gradle index aa9803412957..cbd598140812 100644 --- a/mobile/android/gradle/base/build.gradle +++ b/mobile/android/gradle/base/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 diff --git a/mobile/android/gradle/branding/build.gradle b/mobile/android/gradle/branding/build.gradle index 6c2e1842e1ec..550de863175d 100644 --- a/mobile/android/gradle/branding/build.gradle +++ b/mobile/android/gradle/branding/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 diff --git a/mobile/android/gradle/preprocessed_code/build.gradle b/mobile/android/gradle/preprocessed_code/build.gradle index bb8336b15e93..7b1869661541 100644 --- a/mobile/android/gradle/preprocessed_code/build.gradle +++ b/mobile/android/gradle/preprocessed_code/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 diff --git a/mobile/android/gradle/preprocessed_resources/build.gradle b/mobile/android/gradle/preprocessed_resources/build.gradle index e6080faa2042..c7782be5e6e5 100644 --- a/mobile/android/gradle/preprocessed_resources/build.gradle +++ b/mobile/android/gradle/preprocessed_resources/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 diff --git a/mobile/android/gradle/thirdparty/build.gradle b/mobile/android/gradle/thirdparty/build.gradle index e85e8ab5e7a1..e11a8079cfbe 100644 --- a/mobile/android/gradle/thirdparty/build.gradle +++ b/mobile/android/gradle/thirdparty/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 diff --git a/mobile/android/gradle/thirdparty_adjust_sdk/build.gradle b/mobile/android/gradle/thirdparty_adjust_sdk/build.gradle index cc9b25fdee37..ebc9b26517e0 100644 --- a/mobile/android/gradle/thirdparty_adjust_sdk/build.gradle +++ b/mobile/android/gradle/thirdparty_adjust_sdk/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { targetSdkVersion 22 From f95aacebb1fa575dfdf11948b4602d64103feca0 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Mon, 5 Oct 2015 15:01:23 +0200 Subject: [PATCH 06/35] Bug 1210755 - Mach build: Compile with SDK version 23 and use build tools 23.0.1. r=nalexander --HG-- extra : commitid : KSzxhqJn0iI extra : rebase_source : ccd59642dc6025fd2fec574ef9502a15670fb43e --- configure.in | 2 +- python/mozboot/mozboot/android.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index 69dcd7eacf46..2c86a425ffdb 100644 --- a/configure.in +++ b/configure.in @@ -4095,7 +4095,7 @@ if test -z "$gonkdir" ; then ;; esac - MOZ_ANDROID_SDK(22, 22.0.1) + MOZ_ANDROID_SDK(23, 23.0.1) ;; esac fi diff --git a/python/mozboot/mozboot/android.py b/python/mozboot/mozboot/android.py index aec6ade0737c..e4da0a57b293 100644 --- a/python/mozboot/mozboot/android.py +++ b/python/mozboot/mozboot/android.py @@ -14,8 +14,8 @@ import subprocess # These are the platform and build-tools versions for building # mobile/android, respectively. Try to keep these in synch with the # build system and Mozilla's automation. -ANDROID_TARGET_SDK = '22' -ANDROID_BUILD_TOOLS_VERSION = '22.0.1' +ANDROID_TARGET_SDK = '23' +ANDROID_BUILD_TOOLS_VERSION = '23.0.1' # These are the "Android packages" needed for building Firefox for Android. # Use |android list sdk --extended| to see these identifiers. From f1f8a8717ef10a1fd82a016ac672875ba5518c2e Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Mon, 5 Oct 2015 15:01:49 +0200 Subject: [PATCH 07/35] Bug 1210755 - Touch CLOBBER after updating Android build. r=trivial --HG-- extra : commitid : GNAwQQnuLgi extra : rebase_source : a32ac7f395a4477993b37dfc4d0c75b133768848 --- CLOBBER | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLOBBER b/CLOBBER index e0e8a2acfe89..d7ecd607a4e4 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1182727 - Changed toolchain, needs clobber again :( +Bug 1210755 - Android build: Switch to Android 6.0 SDK / API 23 From 9932ee4e8ca89f6d4b7170e9a59d1bbe0a2f70f0 Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Mon, 5 Oct 2015 15:30:01 +0200 Subject: [PATCH 08/35] Bug 1211219: fix invalid CSS declaration that accidentally got in with work done in bug 1184921. r=Standard8 --- browser/themes/shared/social/chat.inc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/themes/shared/social/chat.inc.css b/browser/themes/shared/social/chat.inc.css index 38e291bf1213..67db29556478 100644 --- a/browser/themes/shared/social/chat.inc.css +++ b/browser/themes/shared/social/chat.inc.css @@ -84,7 +84,7 @@ list-style-image: url("chrome://browser/skin/social/chat-icons.svg#minimize-hover"); } -:hover,:hover:active) { +.chat-minimize-button:hover:active { list-style-image: url("chrome://browser/skin/social/chat-icons.svg#minimize-active"); } From f2ab0b3ffc2771ea26b2e2347d94fd0eb99dc740 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Fri, 2 Oct 2015 05:50:00 +0200 Subject: [PATCH 09/35] Bug 1174674 - Clear out all pending pings when FHR is deactived. r=gfritzsche --- .../telemetry/TelemetryController.jsm | 22 ++- .../components/telemetry/TelemetrySend.jsm | 58 ++++++- .../components/telemetry/TelemetryStorage.jsm | 150 +++++++++++++++--- 3 files changed, 197 insertions(+), 33 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryController.jsm b/toolkit/components/telemetry/TelemetryController.jsm index 18a37f46092a..dcfb77ec217d 100644 --- a/toolkit/components/telemetry/TelemetryController.jsm +++ b/toolkit/components/telemetry/TelemetryController.jsm @@ -909,9 +909,25 @@ var Impl = { // There's nothing we should do if we are enabling upload. return; } - // Send the deletion ping. - this._log.trace("_onUploadPrefChange - Sending deletion ping."); - this.submitExternalPing(PING_TYPE_DELETION, {}, { addClientId: true }); + + let p = Task.spawn(function*() { + try { + // Clear the current pings. + yield TelemetrySend.clearCurrentPings(); + + // Remove all the pending pings, but not the deletion ping. + yield TelemetryStorage.runRemovePendingPingsTask(); + } catch (e) { + this._log.error("_onUploadPrefChange - error clearing pending pings", e); + } finally { + // Always send the deletion ping. + this._log.trace("_onUploadPrefChange - Sending deletion ping."); + this.submitExternalPing(PING_TYPE_DELETION, {}, { addClientId: true }); + } + }.bind(this)); + + this._shutdownBarrier.client.addBlocker( + "TelemetryController: removing pending pings after data upload was disabled", p); }, _attachObservers: function() { diff --git a/toolkit/components/telemetry/TelemetrySend.jsm b/toolkit/components/telemetry/TelemetrySend.jsm index 3d4ff99195db..e93ab41178dd 100644 --- a/toolkit/components/telemetry/TelemetrySend.jsm +++ b/toolkit/components/telemetry/TelemetrySend.jsm @@ -234,6 +234,13 @@ this.TelemetrySend = { return TelemetrySendImpl.setServer(server); }, + /** + * Clear out unpersisted, yet to be sent, pings and cancel outgoing ping requests. + */ + clearCurrentPings: function() { + return TelemetrySendImpl.clearCurrentPings(); + }, + /** * Only used in tests to wait on outgoing pending pings. */ @@ -326,16 +333,19 @@ var SendScheduler = { return Promise.resolve(this._sendTask); }, + start: function() { + this._log.trace("start"); + this._sendsFailed = false; + this._backoffDelay = SEND_TICK_DELAY; + this._shutdown = false; + }, + /** * Only used for testing, resets the state to emulate a restart. */ reset: function() { this._log.trace("reset"); - return this.shutdown().then(() => { - this._sendsFailed = false; - this._backoffDelay = SEND_TICK_DELAY; - this._shutdown = false; - }); + return this.shutdown().then(() => this.start()); }, /** @@ -521,6 +531,8 @@ var SendScheduler = { var TelemetrySendImpl = { _sendingEnabled: false, + // Tracks the shutdown state. + _shutdown: false, _logger: null, // This tracks all pending ping requests to the server. _pendingPingRequests: new Map(), @@ -616,6 +628,8 @@ var TelemetrySendImpl = { }), shutdown: Task.async(function*() { + this._shutdown = true; + for (let topic of this.OBSERVER_TOPICS) { try { Services.obs.removeObserver(this, topic); @@ -643,8 +657,8 @@ var TelemetrySendImpl = { reset: function() { this._log.trace("reset"); + this._shutdown = false; this._currentPings = new Map(); - this._overduePingCount = 0; const histograms = [ @@ -706,6 +720,38 @@ var TelemetrySendImpl = { this._server = server; }, + /** + * Clear out unpersisted, yet to be sent, pings and cancel outgoing ping requests. + */ + clearCurrentPings: Task.async(function*() { + if (this._shutdown) { + this._log.trace("clearCurrentPings - in shutdown, bailing out"); + return; + } + + // Temporarily disable the scheduler. It must not try to reschedule ping sending + // while we're deleting them. + yield SendScheduler.shutdown(); + + // Now that the ping activity has settled, abort outstanding ping requests. + this._cancelOutgoingRequests(); + + // Also, purge current pings. + this._currentPings.clear(); + + // We might have been interrupted and shutdown could have been started. + // We need to bail out in that case to avoid triggering send activity etc. + // at unexpected times. + if (this._shutdown) { + this._log.trace("clearCurrentPings - in shutdown, not spinning SendScheduler up again"); + return; + } + + // Enable the scheduler again and spin the send task. + SendScheduler.start(); + SendScheduler.triggerSendingPings(true); + }), + _cancelOutgoingRequests: function() { // Abort any pending ping XHRs. for (let [id, request] of this._pendingPingRequests) { diff --git a/toolkit/components/telemetry/TelemetryStorage.jsm b/toolkit/components/telemetry/TelemetryStorage.jsm index baf883878e5d..be9c51ec476c 100644 --- a/toolkit/components/telemetry/TelemetryStorage.jsm +++ b/toolkit/components/telemetry/TelemetryStorage.jsm @@ -116,22 +116,9 @@ var Policy = { * always resolves its promise with undefined, and never rejects. */ function waitForAll(it) { - let list = Array.from(it); - let pending = list.length; - if (pending == 0) { - return Promise.resolve(); - } - return new Promise(function(resolve, reject) { - let rfunc = () => { - --pending; - if (pending == 0) { - resolve(); - } - }; - for (let p of list) { - p.then(rfunc, rfunc); - } - }); + let dummy = () => {}; + let promises = [for (p of it) p.catch(dummy)]; + return Promise.all(promises); } this.TelemetryStorage = { @@ -204,6 +191,15 @@ this.TelemetryStorage = { return TelemetryStorageImpl.runEnforcePendingPingsQuotaTask(); }, + /** + * Run the task to remove all the pending pings (except the deletion ping). + * + * @return {Promise} Resolved when the pings are removed. + */ + runRemovePendingPingsTask: function() { + return TelemetryStorageImpl.runRemovePendingPingsTask(); + }, + /** * Reset the storage state in tests. */ @@ -536,6 +532,12 @@ var TelemetryStorageImpl = { // Whether we already scanned the archived pings on disk. _scannedArchiveDirectory: false, + // Track the pending ping removal task. + _removePendingPingsTask: null, + + // This tracks all the pending async ping save activity. + _activePendingPingSaves: new Set(), + // Tracks the pending pings in a Map of (id -> {timestampCreated, type}). // We use this to cache info on pending pings to avoid scanning the disk more than once. _pendingPings: new Map(), @@ -561,12 +563,34 @@ var TelemetryStorageImpl = { */ shutdown: Task.async(function*() { this._shutdown = true; - yield this._abortedSessionSerializer.flushTasks(); - yield this._deletionPingSerializer.flushTasks(); - // If the tasks for archive cleaning or pending ping quota are still running, block on - // them. They will bail out as soon as possible. - yield this._cleanArchiveTask; - yield this._enforcePendingPingsQuotaTask; + + // If the following tasks are still running, block on them. They will bail out as soon + // as possible. + yield this._abortedSessionSerializer.flushTasks().catch(ex => { + this._log.error("shutdown - failed to flush aborted-session writes", ex); + }); + + yield this._deletionPingSerializer.flushTasks().catch(ex => { + this._log.error("shutdown - failed to flush deletion ping writes", ex); + }); + + if (this._cleanArchiveTask) { + yield this._cleanArchiveTask.catch(ex => { + this._log.error("shutdown - the archive cleaning task failed", ex); + }); + } + + if (this._enforcePendingPingsQuotaTask) { + yield this._enforcePendingPingsQuotaTask.catch(ex => { + this._log.error("shutdown - the pending pings quota task failed", ex); + }); + } + + if (this._removePendingPingsTask) { + yield this._removePendingPingsTask.catch(ex => { + this._log.error("shutdown - the pending pings removal task failed", ex); + }); + } }), /** @@ -1171,13 +1195,15 @@ var TelemetryStorageImpl = { }, savePendingPing: function(ping) { - return this.savePing(ping, true).then((path) => { + let p = this.savePing(ping, true).then((path) => { this._pendingPings.set(ping.id, { path: path, lastModificationDate: Policy.now().getTime(), }); this._log.trace("savePendingPing - saved ping with id " + ping.id); }); + this._trackPendingPingSaveTask(p); + return p; }, loadPendingPing: Task.async(function*(id) { @@ -1239,6 +1265,80 @@ var TelemetryStorageImpl = { this._log.error("removePendingPing - failed to remove ping", ex)); }, + /** + * Track any pending ping save tasks through the promise passed here. + * This is needed to block on any outstanding ping save activity. + * + * @param {Object} The save promise to track. + */ + _trackPendingPingSaveTask: function (promise) { + let clear = () => this._activePendingPingSaves.delete(promise); + promise.then(clear, clear); + this._activePendingPingSaves.add(promise); + }, + + /** + * Return a promise that allows to wait on pending pings being saved. + * @return {Object} A promise resolved when all the pending pings save promises + * are resolved. + */ + promisePendingPingSaves: function () { + // Make sure to wait for all the promises, even if they reject. We don't need to log + // the failures here, as they are already logged elsewhere. + return waitForAll(this._activePendingPingSaves); + }, + + /** + * Run the task to remove all the pending pings (except the deletion ping). + * + * @return {Promise} Resolved when the pings are removed. + */ + runRemovePendingPingsTask: Task.async(function*() { + // If we already have a pending pings removal task active, return that. + if (this._removePendingPingsTask) { + return this._removePendingPingsTask; + } + + // Start the task to remove all pending pings. Also make sure to clear the task once done. + try { + this._removePendingPingsTask = this.removePendingPings(); + yield this._removePendingPingsTask; + } finally { + this._removePendingPingsTask = null; + } + }), + + removePendingPings: Task.async(function*() { + this._log.trace("removePendingPings - removing all pending pings"); + + // Wait on pending pings still being saved, so so we don't miss removing them. + yield this.promisePendingPingSaves(); + + // Individually remove existing pings, so we don't interfere with operations expecting + // the pending pings directory to exist. + const directory = TelemetryStorage.pingDirectoryPath; + let iter = new OS.File.DirectoryIterator(directory); + + try { + if (!(yield iter.exists())) { + this._log.trace("removePendingPings - the pending pings directory doesn't exist"); + return; + } + + let files = (yield iter.nextBatch()).filter(e => !e.isDir); + for (let file of files) { + try { + yield OS.File.remove(file.path); + } catch (ex) { + this._log.error("removePendingPings - failed to remove file " + file.path, ex); + continue; + } + } + } finally { + yield iter.close(); + } + }), + loadPendingPingList: function() { // If we already have a pending scanning task active, return that. if (this._scanPendingPingsTask) { @@ -1490,8 +1590,10 @@ var TelemetryStorageImpl = { this._log.trace("saveDeletionPing - ping path: " + gDeletionPingFilePath); yield OS.File.makeDir(gDataReportingDir, { ignoreExisting: true }); - return this._deletionPingSerializer.enqueueTask(() => + let p = this._deletionPingSerializer.enqueueTask(() => this.savePingToFile(ping, gDeletionPingFilePath, true)); + this._trackPendingPingSaveTask(p); + return p; }), /** From 4ad4c62303f725bc2ea500d69214738480fc11aa Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Mon, 5 Oct 2015 09:17:00 +0200 Subject: [PATCH 10/35] Bug 1174674 - Add test coverage. r=gfritzsche --- .../tests/unit/test_TelemetryController.js | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js index 673bacfb04cf..28b7fb5b70c7 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js @@ -143,7 +143,7 @@ add_task(function* test_simplePing() { checkPingFormat(ping, TEST_PING_TYPE, false, false); }); -add_task(function* test_deletionPing() { +add_task(function* test_disableDataUpload() { const isUnified = Preferences.get(PREF_UNIFIED, false); if (!isUnified) { // Skipping the test if unified telemetry is off, as no deletion ping will @@ -166,12 +166,26 @@ add_task(function* test_deletionPing() { // Simulate a failure in sending the deletion ping by disabling the HTTP server. yield PingServer.stop(); + + // Try to send a ping. It will be saved as pending and get deleted when disabling upload. + TelemetryController.submitExternalPing(TEST_PING_TYPE, {}); + // Disable FHR upload to send a deletion ping again. Preferences.set(PREF_FHR_UPLOAD_ENABLED, false); - // Wait for the send task to terminate, flagging it to do so at the next opportunity and - // cancelling any timeouts. + + // Wait on sending activity to settle, as |TelemetryController.reset()| doesn't do that. + yield TelemetrySend.testWaitOnOutgoingPings(); + // Wait for the pending pings to be deleted. Resetting TelemetryController doesn't + // trigger the shutdown, so we need to call it ourselves. + yield TelemetryStorage.shutdown(); + // Simulate a restart, and spin the send task. yield TelemetryController.reset(); + // Disabling Telemetry upload must clear out all the pending pings. + let pendingPings = yield TelemetryStorage.loadPendingPingList(); + Assert.equal(pendingPings.length, 1, + "All the pending pings but the deletion ping should have been deleted"); + // Enable the ping server again. PingServer.start(); // We set the new server using the pref, otherwise it would get reset with From 47f486d1625f88dce5b806abbfefde2fb185632b Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Fri, 2 Oct 2015 06:35:00 +0200 Subject: [PATCH 11/35] Bug 1174674 - Always block Telemetry shutdown on pending ping writes. r=gfritzsche --- toolkit/components/telemetry/TelemetryStorage.jsm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/toolkit/components/telemetry/TelemetryStorage.jsm b/toolkit/components/telemetry/TelemetryStorage.jsm index be9c51ec476c..c771b5f7400a 100644 --- a/toolkit/components/telemetry/TelemetryStorage.jsm +++ b/toolkit/components/telemetry/TelemetryStorage.jsm @@ -591,6 +591,12 @@ var TelemetryStorageImpl = { this._log.error("shutdown - the pending pings removal task failed", ex); }); } + + // Wait on pending pings still being saved. While OS.File should have shutdown + // blockers in place, we a) have seen weird errors being reported that might + // indicate a bad shutdown path and b) might have completion handlers hanging + // off the save operations that don't expect to be late in shutdown. + yield this.promisePendingPingSaves(); }), /** From e887270b9b9e61abb9afb1ea111c92956f610503 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Thu, 1 Oct 2015 10:35:00 +0200 Subject: [PATCH 12/35] Bug 1174674 - Remove octal value syntax errors from Telemetry tests. r=gfritzsche --- .../components/telemetry/tests/unit/test_TelemetryController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js index 28b7fb5b70c7..ff68d83c9bc5 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js @@ -283,7 +283,7 @@ add_task(function* test_archivePings() { Preferences.set(uploadPref, true); Preferences.set(PREF_ARCHIVE_ENABLED, true); - now = new Date(2014, 06, 18, 22, 0, 0); + now = new Date(2014, 6, 18, 22, 0, 0); fakeNow(now); // Restore the non asserting ping handler. PingServer.resetPingHandler(); From 2e4069939ea71f91c1e123d440b566fd4635768f Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Mon, 5 Oct 2015 10:52:43 -0400 Subject: [PATCH 13/35] Bug 1204995 - Update FENNEC_OPEN_URLS_IN_PRIVATE expires_in_version value to actually collect data. r=mfinkle --HG-- extra : commitid : LVeC877NIjp extra : rebase_source : ebde43ab0d72ffba0f62875c30c6f94f82dfc0bd --- toolkit/components/telemetry/Histograms.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index feb317381f20..7939703e4300 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -9038,7 +9038,7 @@ }, "FENNEC_OPEN_URLS_IN_PRIVATE": { "alert_emails": ["margaret@mozilla.com"], - "expires_in_version": "44", + "expires_in_version": "45", "kind": "flag", "description": "Reports the state of the open external links in private tabs preference" }, From 1322b83ce82aa92b49c751eaaaba5097bc26125c Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 17 Jul 2015 16:36:03 -0700 Subject: [PATCH 14/35] Bug 1185163 - Create tri-state tracking protection pref (for Nightly only). r=liuche --HG-- extra : commitid : CZ8A8aeMz9K extra : rebase_source : e59be369b9f4d99d87063eaf781282a700d58252 --- .../base/locales/en-US/android_strings.dtd | 4 ++ .../base/preferences/GeckoPreferences.java | 16 ++++++++ .../android/base/resources/values/arrays.xml | 10 +++++ .../resources/xml/preferences_privacy.xml | 6 +++ mobile/android/base/strings.xml.in | 4 ++ mobile/android/chrome/content/browser.js | 40 +++++++++++++++++++ 6 files changed, 80 insertions(+) diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 3cf5a8487bdb..23ce3fa80573 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -225,6 +225,10 @@ + + + + diff --git a/mobile/android/base/preferences/GeckoPreferences.java b/mobile/android/base/preferences/GeckoPreferences.java index ae7c78be00f5..68ee9db2fb2c 100644 --- a/mobile/android/base/preferences/GeckoPreferences.java +++ b/mobile/android/base/preferences/GeckoPreferences.java @@ -128,6 +128,8 @@ OnSharedPreferenceChangeListener private static final String PREFS_DEVTOOLS_REMOTE_WIFI_ENABLED = "devtools.remote.wifi.enabled"; private static final String PREFS_DISPLAY_TITLEBAR_MODE = "browser.chrome.titlebarMode"; private static final String PREFS_SYNC = NON_PREF_PREFIX + "sync"; + private static final String PREFS_TRACKING_PROTECTION = "privacy.trackingprotection.state"; + private static final String PREFS_TRACKING_PROTECTION_PB = "privacy.trackingprotection.pbmode.enabled"; public static final String PREFS_OPEN_URLS_IN_PRIVATE = NON_PREF_PREFIX + "openExternalURLsPrivately"; public static final String PREFS_VOICE_INPUT_ENABLED = NON_PREF_PREFIX + "voice_input_enabled"; public static final String PREFS_QRCODE_ENABLED = NON_PREF_PREFIX + "qrcode_enabled"; @@ -772,6 +774,20 @@ OnSharedPreferenceChangeListener i--; continue; } + } else if (PREFS_TRACKING_PROTECTION.equals(key)) { + // Remove UI for global TP pref in non-Nightly builds. + if (!AppConstants.NIGHTLY_BUILD) { + preferences.removePreference(pref); + i--; + continue; + } + } else if (PREFS_TRACKING_PROTECTION_PB.equals(key)) { + // Remove UI for private-browsing-only TP pref in Nightly builds. + if (AppConstants.NIGHTLY_BUILD) { + preferences.removePreference(pref); + i--; + continue; + } } else if (PREFS_TELEMETRY_ENABLED.equals(key)) { if (!AppConstants.MOZ_TELEMETRY_REPORTING) { preferences.removePreference(pref); diff --git a/mobile/android/base/resources/values/arrays.xml b/mobile/android/base/resources/values/arrays.xml index 83ee7dfd7c36..81b8fabe2541 100644 --- a/mobile/android/base/resources/values/arrays.xml +++ b/mobile/android/base/resources/values/arrays.xml @@ -50,6 +50,16 @@ @string/pref_cookies_not_accept_foreign @string/pref_cookies_disabled + + 2 + 1 + 0 + + + @string/pref_tracking_protection_enabled + @string/pref_tracking_protection_enabled_pb + @string/pref_tracking_protection_disabled + 0 1 diff --git a/mobile/android/base/resources/xml/preferences_privacy.xml b/mobile/android/base/resources/xml/preferences_privacy.xml index ae42d304d1fc..741ef5f9eb99 100644 --- a/mobile/android/base/resources/xml/preferences_privacy.xml +++ b/mobile/android/base/resources/xml/preferences_privacy.xml @@ -13,6 +13,12 @@ android:summary="@string/pref_tracking_protection_summary" android:persistent="false" /> + + &pref_donottrack_title; &pref_donottrack_summary; + &pref_tracking_protection_enabled; + &pref_tracking_protection_enabled_pb; + &pref_tracking_protection_disabled; + &pref_char_encoding; &pref_char_encoding_on; &pref_char_encoding_off; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 9c69e41be5fb..08632090f2d6 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1414,6 +1414,11 @@ var BrowserApp = { }, this); }, + // These values come from pref_tracking_protection_entries in arrays.xml. + PREF_TRACKING_PROTECTION_ENABLED: "2", + PREF_TRACKING_PROTECTION_ENABLED_PB: "1", + PREF_TRACKING_PROTECTION_DISABLED: "0", + handlePreferencesRequest: function handlePreferencesRequest(aRequestId, aPrefNames, aListen) { @@ -1452,6 +1457,18 @@ var BrowserApp = { pref.value = MasterPassword.enabled; prefs.push(pref); continue; + case "privacy.trackingprotection.state": { + pref.type = "string"; + if (Services.prefs.getBoolPref("privacy.trackingprotection.enabled")) { + pref.value = this.PREF_TRACKING_PROTECTION_ENABLED; + } else if (Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled")) { + pref.value = this.PREF_TRACKING_PROTECTION_ENABLED_PB; + } else { + pref.value = this.PREF_TRACKING_PROTECTION_DISABLED; + } + prefs.push(pref); + continue; + } // Crash reporter submit pref must be fetched from nsICrashReporter service. case "datareporting.crashreporter.submitEnabled": let crashReporterBuilt = "nsICrashReporter" in Ci && Services.appinfo instanceof Ci.nsICrashReporter; @@ -1534,6 +1551,29 @@ var BrowserApp = { MasterPassword.setPassword(json.value); return; + // "privacy.trackingprotection.state" is not a "real" pref name, but it's used in the setting menu. + // By default "privacy.trackingprotection.pbmode.enabled" is true, + // and "privacy.trackingprotection.enabled" is false. + case "privacy.trackingprotection.state": { + switch (json.value) { + // Tracking protection disabled. + case this.PREF_TRACKING_PROTECTION_DISABLED: + Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", false); + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); + break; + // Tracking protection only in private browsing, + case this.PREF_TRACKING_PROTECTION_ENABLED_PB: + Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true); + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); + break; + // Tracking protection everywhere. + case this.PREF_TRACKING_PROTECTION_ENABLED: + Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true); + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true); + break; + } + return; + } // Enabling or disabling suggestions will prevent future prompts case SearchEngines.PREF_SUGGEST_ENABLED: Services.prefs.setBoolPref(SearchEngines.PREF_SUGGEST_PROMPTED, true); From 5de966245381c209b2fa671e090de0cd3a6bdb59 Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 1 Oct 2015 00:20:54 +0300 Subject: [PATCH 15/35] Bug 1137333 Clear history button style changes r=margaret --HG-- extra : commitid : BQPwsGgDeRN extra : rebase_source : 13f661cb03504ceec7fe85fddb4346b413f5ba2a --- .../drawable/home_history_clear_button_bg.xml | 23 +++++++++++++++++++ .../layout/home_history_clear_button.xml | 10 ++++++++ .../resources/layout/home_history_panel.xml | 12 +--------- .../layout/home_history_split_pane_panel.xml | 9 +------- .../base/resources/values-v16/styles.xml | 4 ---- .../android/base/resources/values/styles.xml | 21 +++++------------ 6 files changed, 41 insertions(+), 38 deletions(-) create mode 100644 mobile/android/base/resources/drawable/home_history_clear_button_bg.xml create mode 100644 mobile/android/base/resources/layout/home_history_clear_button.xml diff --git a/mobile/android/base/resources/drawable/home_history_clear_button_bg.xml b/mobile/android/base/resources/drawable/home_history_clear_button_bg.xml new file mode 100644 index 000000000000..f722d8da6ad0 --- /dev/null +++ b/mobile/android/base/resources/drawable/home_history_clear_button_bg.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mobile/android/base/resources/layout/home_history_clear_button.xml b/mobile/android/base/resources/layout/home_history_clear_button.xml new file mode 100644 index 000000000000..c21e6d1fdf50 --- /dev/null +++ b/mobile/android/base/resources/layout/home_history_clear_button.xml @@ -0,0 +1,10 @@ + + + +