From ca42c5e66e62886969a52b712270179a0a6f511f Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 28 Oct 2013 09:25:48 -0400 Subject: [PATCH 01/21] Bug 930916 - Add a function for getting the PaintedSurface as a data URI. r=gbrown --- build/mobile/robocop/PaintedSurface.java.in | 41 ++++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/build/mobile/robocop/PaintedSurface.java.in b/build/mobile/robocop/PaintedSurface.java.in index deca9f12b33..81302dad1bf 100644 --- a/build/mobile/robocop/PaintedSurface.java.in +++ b/build/mobile/robocop/PaintedSurface.java.in @@ -5,7 +5,12 @@ package @ANDROID_PACKAGE_NAME@; +import android.graphics.Bitmap; +import android.util.Base64; +import android.util.Base64OutputStream; + import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.nio.MappedByteBuffer; @@ -44,6 +49,15 @@ public class PaintedSurface { return mHeight; } + private int pixelAtIndex(int index) { + int b1 = mPixelBuffer.get(index) & 0xFF; + int b2 = mPixelBuffer.get(index + 1) & 0xFF; + int b3 = mPixelBuffer.get(index + 2) & 0xFF; + int b4 = mPixelBuffer.get(index + 3) & 0xFF; + int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0); + return value; + } + public final int getPixelAt(int x, int y) { if (mPixelBuffer == null) { throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer"); @@ -60,12 +74,27 @@ public class PaintedSurface { // The rows are reversed so row 0 is at the end and we start with the last row. // This is why we do mHeight-y; int index = (x + ((mHeight - y - 1) * mWidth)) * 4; - int b1 = mPixelBuffer.get(index) & 0xFF; - int b2 = mPixelBuffer.get(index + 1) & 0xFF; - int b3 = mPixelBuffer.get(index + 2) & 0xFF; - int b4 = mPixelBuffer.get(index + 3) & 0xFF; - int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0); - return value; + return pixelAtIndex(index); + } + + public final String asDataUri() { + try { + Bitmap bm = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); + for (int y = 0; y < mHeight; y++) { + for (int x = 0; x < mWidth; x++) { + int index = (x + ((mHeight - y - 1) * mWidth)) * 4; + bm.setPixel(x, y, pixelAtIndex(index)); + } + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write("data:image/png;base64,".getBytes()); + Base64OutputStream b64 = new Base64OutputStream(out, Base64.NO_WRAP); + bm.compress(Bitmap.CompressFormat.PNG, 100, b64); + return new String(out.toByteArray()); + } catch (Exception e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e); + throw new RoboCopException("Unable to convert surface to a PNG data:uri"); + } } public void close() { From 027ee7f539cda585c50e01d38d668d9e7655ceea Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Sat, 26 Oct 2013 09:13:43 -0500 Subject: [PATCH 02/21] Bug 930588 - Dark/Light theme follow-up updates for CodeMirror --- browser/themes/shared/devtools/dark-theme.css | 22 ++++++++++------ .../themes/shared/devtools/light-theme.css | 25 +++++++++++++------ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/browser/themes/shared/devtools/dark-theme.css b/browser/themes/shared/devtools/dark-theme.css index 243fef2bfb5..9224b09ad44 100644 --- a/browser/themes/shared/devtools/dark-theme.css +++ b/browser/themes/shared/devtools/dark-theme.css @@ -63,7 +63,8 @@ .theme-comment, .cm-s-mozilla .cm-meta, -.cm-s-mozilla .cm-hr { /* grey */ +.cm-s-mozilla .cm-hr, +.cm-s-mozilla .cm-comment { /* grey */ color: #5c6773; } @@ -79,8 +80,7 @@ .theme-fg-color1, .cm-s-mozilla .cm-variable-2, -.cm-s-mozilla .cm-quote, -.cm-s-mozilla .CodeMirror-matchingbracket { /* green */ +.cm-s-mozilla .cm-quote { /* green */ color: #5c9966; } @@ -101,8 +101,7 @@ color: #a673bf; } -.theme-fg-color4, -.cm-s-mozilla .cm-comment { /* purple/violet */ +.theme-fg-color4 { /* purple/violet */ color: #6270b2; } @@ -119,7 +118,6 @@ } .theme-fg-color7, -.cm-s-mozilla .CodeMirror-nonmatchingbracket, .cm-s-mozilla .cm-string-2, .cm-s-mozilla .cm-error { /* Red */ color: #bf5656; @@ -160,12 +158,20 @@ background: rgb(185, 215, 253); } -.dcm-s-mozilla .CodeMirror-selected { /* selected text (unfocused) */ +.cm-s-mozilla .CodeMirror-selected { /* selected text (unfocused) */ background: rgb(176, 176, 176); } .CodeMirror-activeline-background { /* selected color with alpha */ - background: rgba(185, 215, 253, .05); + background: rgba(185, 215, 253, .15); +} + +.cm-s-mozilla .CodeMirror-linenumber { /* line number text */ + color: #5f7387; +} + +.cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */ + border-right-color: #343c45; } .cm-s-markup-view pre { diff --git a/browser/themes/shared/devtools/light-theme.css b/browser/themes/shared/devtools/light-theme.css index a2cfd9caec0..dbe0e1036ef 100644 --- a/browser/themes/shared/devtools/light-theme.css +++ b/browser/themes/shared/devtools/light-theme.css @@ -63,7 +63,8 @@ .theme-comment, .cm-s-mozilla .cm-meta, -.cm-s-mozilla .cm-hr { /* grey */ +.cm-s-mozilla .cm-hr, +.cm-s-mozilla .cm-comment { /* grey */ color: hsl(90,2%,46%); } @@ -79,8 +80,7 @@ .theme-fg-color1, .cm-s-mozilla .cm-variable-2, -.cm-s-mozilla .cm-quote, -.cm-s-mozilla .CodeMirror-matchingbracket { /* green */ +.cm-s-mozilla .cm-quote { /* green */ color: hsl(72,100%,27%); } @@ -101,8 +101,7 @@ color: hsl(208,81%,21%) } -.theme-fg-color4, -.cm-s-mozilla .cm-comment { /* Orange */ +.theme-fg-color4 { /* Orange */ color: hsl(24,85%,39%); } @@ -119,7 +118,6 @@ } .theme-fg-color7, -.cm-s-mozilla .CodeMirror-nonmatchingbracket, .cm-s-mozilla .cm-string-2, .cm-s-mozilla .cm-error { /* Red */ color: #bf5656; @@ -165,7 +163,20 @@ } .CodeMirror-activeline-background { /* selected color with alpha */ - background: rgba(185, 215, 253, .4); + background: rgba(185, 215, 253, .35); +} + +div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */ + outline: solid 1px rgba(0, 0, 0, .25); + color: black; +} + +.cm-s-mozilla .CodeMirror-linenumber { /* line number text */ + color: #667380; +} + +.cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */ + border-right-color: #a6a6a6; } .cm-s-markup-view pre { From 48766c2fcb56711f5115624392b6cd28853d4aed Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Mon, 28 Oct 2013 09:36:50 -0700 Subject: [PATCH 03/21] Bug 922981 - Split marDownload.js into individual tests. r=bbondy --HG-- rename : toolkit/mozapps/update/tests/unit_aus_update/downloadMar.js => toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js rename : toolkit/mozapps/update/tests/unit_aus_update/downloadMar.js => toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js --- .../downloadAndHashCheckMar.js | 215 ++++++++++++++++++ ...dMar.js => downloadInterruptedRecovery.js} | 177 +------------- .../tests/unit_aus_update/head_update.js | 27 +++ .../update/tests/unit_aus_update/xpcshell.ini | 3 +- 4 files changed, 253 insertions(+), 169 deletions(-) create mode 100644 toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js rename toolkit/mozapps/update/tests/unit_aus_update/{downloadMar.js => downloadInterruptedRecovery.js} (60%) diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js new file mode 100644 index 00000000000..e4ab15fa80f --- /dev/null +++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js @@ -0,0 +1,215 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var gNextRunFunc; +var gExpectedStatusResult; + +function run_test() { + setupTestCommon(true); + + logTestInfo("testing mar download and mar hash verification"); + + Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false); + // The HTTP server is only used for the mar file downloads since it is slow + start_httpserver(); + setUpdateURLOverride(gURLData + "update.xml"); + // The mock XMLHttpRequest is MUCH faster + overrideXHR(callHandleEvent); + standardInit(); + do_execute_soon(run_test_pt1); +} + +// The HttpServer must be stopped before calling do_test_finished +function finish_test() { + stop_httpserver(do_test_finished); +} + +function end_test() { + cleanupTestCommon(); +} + +// Callback function used by the custom XMLHttpRequest implementation to +// call the nsIDOMEventListener's handleEvent method for onload. +function callHandleEvent() { + gXHR.status = 400; + gXHR.responseText = gResponseBody; + try { + var parser = AUS_Cc["@mozilla.org/xmlextras/domparser;1"]. + createInstance(AUS_Ci.nsIDOMParser); + gXHR.responseXML = parser.parseFromString(gResponseBody, "application/xml"); + } + catch(e) { + } + var e = { target: gXHR }; + gXHR.onload(e); +} + +// Helper function for testing mar downloads that have the correct size +// specified in the update xml. +function run_test_helper_pt1(aMsg, aExpectedStatusResult, aNextRunFunc) { + gUpdates = null; + gUpdateCount = null; + gStatusResult = null; + gCheckFunc = check_test_helper_pt1_1; + gNextRunFunc = aNextRunFunc; + gExpectedStatusResult = aExpectedStatusResult; + logTestInfo(aMsg, Components.stack.caller); + gUpdateChecker.checkForUpdates(updateCheckListener, true); +} + +function check_test_helper_pt1_1() { + do_check_eq(gUpdateCount, 1); + gCheckFunc = check_test_helper_pt1_2; + var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount); + var state = gAUS.downloadUpdate(bestUpdate, false); + if (state == STATE_NONE || state == STATE_FAILED) + do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state); + gAUS.addDownloadListener(downloadListener); +} + +function check_test_helper_pt1_2() { + do_check_eq(gStatusResult, gExpectedStatusResult); + gAUS.removeDownloadListener(downloadListener); + gNextRunFunc(); +} + +// The following 3 functions are a workaround for GONK due to Bug 828858 and +// can be removed after it is fixed and the callers are changed to use the +// regular helper functions. +function run_test_helper_bug828858_pt1(aMsg, aExpectedStatusResult, aNextRunFunc) { + gUpdates = null; + gUpdateCount = null; + gStatusResult = null; + gCheckFunc = check_test_helper_bug828858_pt1_1; + gNextRunFunc = aNextRunFunc; + gExpectedStatusResult = aExpectedStatusResult; + logTestInfo(aMsg, Components.stack.caller); + gUpdateChecker.checkForUpdates(updateCheckListener, true); +} + +function check_test_helper_bug828858_pt1_1() { + do_check_eq(gUpdateCount, 1); + gCheckFunc = check_test_helper_bug828858_pt1_2; + var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount); + var state = gAUS.downloadUpdate(bestUpdate, false); + if (state == STATE_NONE || state == STATE_FAILED) + do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state); + gAUS.addDownloadListener(downloadListener); +} + +function check_test_helper_bug828858_pt1_2() { + if (gStatusResult == AUS_Cr.NS_ERROR_CONTENT_CORRUPTED) { + do_check_eq(gStatusResult, AUS_Cr.NS_ERROR_CONTENT_CORRUPTED); + } else { + do_check_eq(gStatusResult, gExpectedStatusResult); + } + gAUS.removeDownloadListener(downloadListener); + gNextRunFunc(); +} + +function setResponseBody(aHashFunction, aHashValue, aSize) { + var patches = getRemotePatchString(null, null, + aHashFunction, aHashValue, aSize); + var updates = getRemoteUpdateString(patches); + gResponseBody = getRemoteUpdatesXMLString(updates); +} + +// mar download with a valid MD5 hash +function run_test_pt1() { + setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with a valid MD5 hash", + AUS_Cr.NS_OK, run_test_pt2); +} + +// mar download with an invalid MD5 hash +function run_test_pt2() { + setResponseBody("MD5", MD5_HASH_SIMPLE_MAR + "0"); + run_test_helper_pt1("mar download with an invalid MD5 hash", + AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt3); +} + +// mar download with a valid SHA1 hash +function run_test_pt3() { + setResponseBody("SHA1", SHA1_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with a valid SHA1 hash", + AUS_Cr.NS_OK, run_test_pt4); +} + +// mar download with an invalid SHA1 hash +function run_test_pt4() { + setResponseBody("SHA1", SHA1_HASH_SIMPLE_MAR + "0"); + run_test_helper_pt1("mar download with an invalid SHA1 hash", + AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt5); +} + +// mar download with a valid SHA256 hash +function run_test_pt5() { + setResponseBody("SHA256", SHA256_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with a valid SHA256 hash", + AUS_Cr.NS_OK, run_test_pt6); +} + +// mar download with an invalid SHA256 hash +function run_test_pt6() { + setResponseBody("SHA256", SHA256_HASH_SIMPLE_MAR + "0"); + run_test_helper_pt1("mar download with an invalid SHA256 hash", + AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt7); +} + +// mar download with a valid SHA384 hash +function run_test_pt7() { + setResponseBody("SHA384", SHA384_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with a valid SHA384 hash", + AUS_Cr.NS_OK, run_test_pt8); +} + +// mar download with an invalid SHA384 hash +function run_test_pt8() { + setResponseBody("SHA384", SHA384_HASH_SIMPLE_MAR + "0"); + run_test_helper_pt1("mar download with an invalid SHA384 hash", + AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt9); +} + +// mar download with a valid SHA512 hash +function run_test_pt9() { + setResponseBody("SHA512", SHA512_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with a valid SHA512 hash", + AUS_Cr.NS_OK, run_test_pt10); +} + +// mar download with an invalid SHA512 hash +function run_test_pt10() { + setResponseBody("SHA512", SHA512_HASH_SIMPLE_MAR + "0"); + run_test_helper_pt1("mar download with an invalid SHA512 hash", + AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt11); +} + +// mar download with the mar not found +function run_test_pt11() { + var patches = getRemotePatchString(null, gURLData + "missing.mar"); + var updates = getRemoteUpdateString(patches); + gResponseBody = getRemoteUpdatesXMLString(updates); + run_test_helper_pt1("mar download with the mar not found", + AUS_Cr.NS_ERROR_UNEXPECTED, run_test_pt12); +} + +// mar download with a valid MD5 hash but invalid file size +function run_test_pt12() { + const arbitraryFileSize = 1024000; + setResponseBody("MD5", MD5_HASH_SIMPLE_MAR, arbitraryFileSize); + if (IS_TOOLKIT_GONK) { + // There seems to be a race on the web server side when the patchFile is + // stored on the SDCard. Sometimes, the webserver will serve up an error + // 416 and the contents of the file, and sometimes it will serve up an error + // 200 and no contents. This can cause either NS_ERROR_UNEXPECTED or + // NS_ERROR_CONTENT_CORRUPTED. + // Bug 828858 was filed to follow up on this issue. + run_test_helper_bug828858_pt1("mar download with a valid MD5 hash but invalid file size", + AUS_Cr.NS_ERROR_UNEXPECTED, finish_test); + } else { + run_test_helper_pt1("mar download with a valid MD5 hash but invalid file size", + AUS_Cr.NS_ERROR_UNEXPECTED, finish_test); + } +} diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadMar.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js similarity index 60% rename from toolkit/mozapps/update/tests/unit_aus_update/downloadMar.js rename to toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js index 777a33000b1..943f6f84436 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/downloadMar.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js @@ -7,15 +7,14 @@ const INC_CONTRACT_ID = "@mozilla.org/network/incremental-download;1"; -var gNextRunFunc; -var gStatusResult; -var gExpectedStatusResult; var gIncrementalDownloadClassID, gIncOldFactory; - // gIncrementalDownloadErrorType is used to loop through each of the connection // error types in the Mock incremental downloader. var gIncrementalDownloadErrorType = 0; +var gNextRunFunc; +var gExpectedStatusResult; + function run_test() { setupTestCommon(true); @@ -87,40 +86,6 @@ function check_test_helper_pt1_2() { gNextRunFunc(); } -// The following 3 functions are a workaround for GONK due to Bug 828858 and -// can be removed after it is fixed and the callers are changed to use the -// regular helper functions. -function run_test_helper_bug828858_pt1(aMsg, aExpectedStatusResult, aNextRunFunc) { - gUpdates = null; - gUpdateCount = null; - gStatusResult = null; - gCheckFunc = check_test_helper_bug828858_pt1_1; - gNextRunFunc = aNextRunFunc; - gExpectedStatusResult = aExpectedStatusResult; - logTestInfo(aMsg, Components.stack.caller); - gUpdateChecker.checkForUpdates(updateCheckListener, true); -} - -function check_test_helper_bug828858_pt1_1() { - do_check_eq(gUpdateCount, 1); - gCheckFunc = check_test_helper_bug828858_pt1_2; - var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount); - var state = gAUS.downloadUpdate(bestUpdate, false); - if (state == STATE_NONE || state == STATE_FAILED) - do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state); - gAUS.addDownloadListener(downloadListener); -} - -function check_test_helper_bug828858_pt1_2() { - if (gStatusResult == AUS_Cr.NS_ERROR_CONTENT_CORRUPTED) { - do_check_eq(gStatusResult, AUS_Cr.NS_ERROR_CONTENT_CORRUPTED); - } else { - do_check_eq(gStatusResult, gExpectedStatusResult); - } - gAUS.removeDownloadListener(downloadListener); - gNextRunFunc(); -} - function setResponseBody(aHashFunction, aHashValue, aSize) { var patches = getRemotePatchString(null, null, aHashFunction, aHashValue, aSize); @@ -128,104 +93,6 @@ function setResponseBody(aHashFunction, aHashValue, aSize) { gResponseBody = getRemoteUpdatesXMLString(updates); } -// mar download with a valid MD5 hash -function run_test_pt1() { - setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); - run_test_helper_pt1("mar download with a valid MD5 hash", - AUS_Cr.NS_OK, run_test_pt2); -} - -// mar download with an invalid MD5 hash -function run_test_pt2() { - setResponseBody("MD5", MD5_HASH_SIMPLE_MAR + "0"); - run_test_helper_pt1("mar download with an invalid MD5 hash", - AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt3); -} - -// mar download with a valid SHA1 hash -function run_test_pt3() { - setResponseBody("SHA1", SHA1_HASH_SIMPLE_MAR); - run_test_helper_pt1("mar download with a valid SHA1 hash", - AUS_Cr.NS_OK, run_test_pt4); -} - -// mar download with an invalid SHA1 hash -function run_test_pt4() { - setResponseBody("SHA1", SHA1_HASH_SIMPLE_MAR + "0"); - run_test_helper_pt1("mar download with an invalid SHA1 hash", - AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt5); -} - -// mar download with a valid SHA256 hash -function run_test_pt5() { - setResponseBody("SHA256", SHA256_HASH_SIMPLE_MAR); - run_test_helper_pt1("mar download with a valid SHA256 hash", - AUS_Cr.NS_OK, run_test_pt6); -} - -// mar download with an invalid SHA256 hash -function run_test_pt6() { - setResponseBody("SHA256", SHA256_HASH_SIMPLE_MAR + "0"); - run_test_helper_pt1("mar download with an invalid SHA256 hash", - AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt7); -} - -// mar download with a valid SHA384 hash -function run_test_pt7() { - setResponseBody("SHA384", SHA384_HASH_SIMPLE_MAR); - run_test_helper_pt1("mar download with a valid SHA384 hash", - AUS_Cr.NS_OK, run_test_pt8); -} - -// mar download with an invalid SHA384 hash -function run_test_pt8() { - setResponseBody("SHA384", SHA384_HASH_SIMPLE_MAR + "0"); - run_test_helper_pt1("mar download with an invalid SHA384 hash", - AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt9); -} - -// mar download with a valid SHA512 hash -function run_test_pt9() { - setResponseBody("SHA512", SHA512_HASH_SIMPLE_MAR); - run_test_helper_pt1("mar download with a valid SHA512 hash", - AUS_Cr.NS_OK, run_test_pt10); -} - -// mar download with an invalid SHA384 hash -function run_test_pt10() { - setResponseBody("SHA512", SHA512_HASH_SIMPLE_MAR + "0"); - run_test_helper_pt1("mar download with an invalid SHA512 hash", - AUS_Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt11); -} - -// mar download with the mar not found -function run_test_pt11() { - var patches = getRemotePatchString(null, gURLData + "missing.mar"); - var updates = getRemoteUpdateString(patches); - gResponseBody = getRemoteUpdatesXMLString(updates); - run_test_helper_pt1("mar download with the mar not found", - AUS_Cr.NS_ERROR_UNEXPECTED, run_test_pt12); -} - -// mar download with a valid MD5 hash but invalid file size -function run_test_pt12() { - const arbitraryFileSize = 1024000; - setResponseBody("MD5", MD5_HASH_SIMPLE_MAR, arbitraryFileSize); - if (IS_TOOLKIT_GONK) { - // There seems to be a race on the web server side when the patchFile is - // stored on the SDCard. Sometimes, the webserver will serve up an error - // 416 and the contents of the file, and sometimes it will serve up an error - // 200 and no contents. This can cause either NS_ERROR_UNEXPECTED or - // NS_ERROR_CONTENT_CORRUPTED. - // Bug 828858 was filed to follow up on this issue. - run_test_helper_bug828858_pt1("mar download with a valid MD5 hash but invalid file size", - AUS_Cr.NS_ERROR_UNEXPECTED, run_test_pt13); - } else { - run_test_helper_pt1("mar download with a valid MD5 hash but invalid file size", - AUS_Cr.NS_ERROR_UNEXPECTED, run_test_pt13); - } -} - var newFactory = { createInstance: function(aOuter, aIID) { if (aOuter) @@ -263,7 +130,7 @@ function cleanupMockIncrementalDownload() { * interrupts work correctly in updater code. The implementation of * the mock incremental downloader is very simple, it simply copies * the file to the destination location. -*/ + */ function IncrementalDownload() { this.wrappedJSObject = this; @@ -379,15 +246,15 @@ IncrementalDownload.prototype = { } // Test disconnecting during an update -function run_test_pt13() { +function run_test_pt1() { initMockIncrementalDownload(); setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); run_test_helper_pt1("mar download with connection interruption", - AUS_Cr.NS_OK, run_test_pt14); + AUS_Cr.NS_OK, run_test_pt2); } // Test disconnecting during an update -function run_test_pt14() { +function run_test_pt2() { gIncrementalDownloadErrorType = 0; Services.prefs.setIntPref(PREF_APP_UPDATE_SOCKET_ERRORS, 2); Services.prefs.setIntPref(PREF_APP_UPDATE_RETRY_TIMEOUT, 0); @@ -404,39 +271,13 @@ function run_test_pt14() { expectedResult = AUS_Cr.NS_ERROR_NET_RESET; } run_test_helper_pt1("mar download with connection interruption without recovery", - expectedResult, run_test_pt15); + expectedResult, run_test_pt3); } // Test entering offline mode while downloading -function run_test_pt15() { +function run_test_pt3() { gIncrementalDownloadErrorType = 4; setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); run_test_helper_pt1("mar download with offline mode", AUS_Cr.NS_OK, finish_test); } - -/* Update download listener - nsIRequestObserver */ -const downloadListener = { - onStartRequest: function DL_onStartRequest(request, context) { - }, - - onProgress: function DL_onProgress(request, context, progress, maxProgress) { - }, - - onStatus: function DL_onStatus(request, context, status, statusText) { - }, - - onStopRequest: function DL_onStopRequest(request, context, status) { - gStatusResult = status; - // Use a timeout to allow the request to complete - do_execute_soon(gCheckFunc); - }, - - QueryInterface: function DL_QueryInterface(iid) { - if (!iid.equals(AUS_Ci.nsIRequestObserver) && - !iid.equals(AUS_Ci.nsIProgressEventSink) && - !iid.equals(AUS_Ci.nsISupports)) - throw AUS_Cr.NS_ERROR_NO_INTERFACE; - return this; - } -}; diff --git a/toolkit/mozapps/update/tests/unit_aus_update/head_update.js b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js index b3b83f6f425..a2dfb1d5a7e 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/head_update.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js @@ -147,6 +147,7 @@ var gUpdateCount; var gUpdates; var gStatusCode; var gStatusText; +var gStatusResult; // Variables are used instead of contants so tests can override these values var gCallbackBinFile = "callback_app" + BIN_SUFFIX; @@ -1951,6 +1952,32 @@ const updateCheckListener = { } }; +/* Update download listener - nsIRequestObserver */ +const downloadListener = { + onStartRequest: function DL_onStartRequest(request, context) { + }, + + onProgress: function DL_onProgress(request, context, progress, maxProgress) { + }, + + onStatus: function DL_onStatus(request, context, status, statusText) { + }, + + onStopRequest: function DL_onStopRequest(request, context, status) { + gStatusResult = status; + // Use a timeout to allow the request to complete + do_execute_soon(gCheckFunc); + }, + + QueryInterface: function DL_QueryInterface(aIID) { + if (!aIID.equals(AUS_Ci.nsIRequestObserver) && + !aIID.equals(AUS_Ci.nsIProgressEventSink) && + !aIID.equals(AUS_Ci.nsISupports)) + throw AUS_Cr.NS_ERROR_NO_INTERFACE; + return this; + } +}; + /** * Helper for starting the http server used by the tests */ diff --git a/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini b/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini index 6291a59332e..104fd13b4f1 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini +++ b/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini @@ -12,7 +12,7 @@ generated-files = head_update.js [updateCheckOnLoadOnErrorStatusText.js] [updateManagerXML.js] [remoteUpdateXML.js] -[downloadMar.js] +[downloadAndHashCheckMar.js] [cleanupDownloadingForOlderAppVersion.js] [cleanupDownloadingForDifferentChannel.js] [cleanupDownloadingForSameVersionAndBuildID.js] @@ -20,6 +20,7 @@ generated-files = head_update.js [cleanupPendingVersionFileIncorrectStatus.js] [cleanupSuccessLogMove.js] [cleanupSuccessLogsFIFO.js] +[downloadInterruptedRecovery.js] [downloadResumeForSameAppVersion.js] [downloadCompleteAfterPartialFailure.js] skip-if = toolkit == 'gonk' From 219ebf110f3092e23f3c44e7ab043fc528dd8e92 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Thu, 17 Oct 2013 13:32:57 -0700 Subject: [PATCH 04/21] Bug 924480 - Part 1: Make search_container focusable before browser_toolbar. r=bnicholson --- mobile/android/base/BrowserToolbar.java | 1 + .../base/resources/layout/gecko_app.xml | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index b1c88879b6c..f420da4a4ba 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -410,6 +410,7 @@ public class BrowserToolbar extends GeckoRelativeLayout } if (keyCode == KeyEvent.KEYCODE_BACK) { + // Drop the virtual keyboard. clearFocus(); return true; } diff --git a/mobile/android/base/resources/layout/gecko_app.xml b/mobile/android/base/resources/layout/gecko_app.xml index 8d3407933c2..6b9ca9d8df2 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml +++ b/mobile/android/base/resources/layout/gecko_app.xml @@ -55,20 +55,25 @@ android:layout_alignParentBottom="true"> - + + + - - Date: Mon, 28 Oct 2013 12:06:25 -0700 Subject: [PATCH 05/21] Bug 924480 - Part 1.5: Make HomePager focusable. r=lucasr --- mobile/android/base/home/HomePager.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index d07debd36d5..52512320de4 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -96,6 +96,13 @@ public class HomePager extends ViewPager { // This is to keep all 4 pages in memory after they are // selected in the pager. setOffscreenPageLimit(3); + + // We can call HomePager.requestFocus to steal focus from the URL bar and drop the soft + // keyboard. However, if there are no focusable views (e.g. an empty reading list), the + // URL bar will be refocused. Therefore, we make the HomePager container focusable to + // ensure there is always a focusable view. This would ordinarily be done via an XML + // attribute, but it is not working properly. + setFocusableInTouchMode(true); } @Override @@ -324,10 +331,7 @@ public class HomePager extends ViewPager { @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - // XXX: Drop the soft keyboard by stealing focus. Note that the HomePager (via XML - // attr) is focusable after its descendants allowing requestFocus to succeed and drop - // the soft keyboard even if there are no other focusable views on the screen (e.g. - // the Reading List is empty). + // Drop the soft keyboard by stealing focus from the URL bar. requestFocus(); } From df0d1119e37ed8b079b18a1f20f4d10ec25bec27 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Fri, 18 Oct 2013 09:40:28 -0700 Subject: [PATCH 06/21] Bug 924480 - Part 2: Disable TalkBack access to Gecko with HomePager displayed. r=eeejay,lucasr --- mobile/android/base/BrowserApp.java | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index cb9183e68bf..a81c8fa11e8 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -167,6 +167,12 @@ abstract public class BrowserApp extends GeckoApp private BrowserHealthReporter mBrowserHealthReporter; + // The animator used to toggle HomePager visibility has a race where if the HomePager is shown + // (starting the animation), the HomePager is hidden, and the HomePager animation completes, + // both the web content and the HomePager will be hidden. This flag is used to prevent the + // race by determining if the web content should be hidden at the animation's end. + private boolean mHideWebContentOnAnimationEnd = false; + private SiteIdentityPopup mSiteIdentityPopup; public SiteIdentityPopup getSiteIdentityPopup() { @@ -1574,7 +1580,38 @@ abstract public class BrowserApp extends GeckoApp final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub); mHomePager = (HomePager) homePagerStub.inflate(); } + mHomePager.show(getSupportFragmentManager(), page, animator); + + // Hide the web content so it cannot be focused by screen readers. + hideWebContentOnPropertyAnimationEnd(animator); + } + + private void hideWebContentOnPropertyAnimationEnd(final PropertyAnimator animator) { + if (animator == null) { + hideWebContent(); + return; + } + + animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { + @Override + public void onPropertyAnimationStart() { + mHideWebContentOnAnimationEnd = true; + } + + @Override + public void onPropertyAnimationEnd() { + if (mHideWebContentOnAnimationEnd) { + hideWebContent(); + } + } + }); + } + + private void hideWebContent() { + // The view is set to INVISIBLE, rather than GONE, to avoid + // the additional requestLayout() call. + mLayerView.setVisibility(View.INVISIBLE); } private void hideHomePager() { @@ -1587,6 +1624,12 @@ abstract public class BrowserApp extends GeckoApp return; } + // Prevent race in hiding web content - see declaration for more info. + mHideWebContentOnAnimationEnd = false; + + // Display the previously hidden web content (which prevented screen reader access). + mLayerView.setVisibility(View.VISIBLE); + if (mHomePager != null) { mHomePager.hide(); } From f11e59968fccf4bc5729c77ea8d4cb3ef7b153a7 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:20:26 -0700 Subject: [PATCH 07/21] Bug 915918 - Part 1: Select previously selected tab upon editing mode exit. r=lucasr --- mobile/android/base/BrowserApp.java | 31 +++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index a81c8fa11e8..de313f86922 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -167,6 +167,9 @@ abstract public class BrowserApp extends GeckoApp private BrowserHealthReporter mBrowserHealthReporter; + // The tab to be selected on editing mode exit. + private Integer mTargetTabForEditingMode = null; + // The animator used to toggle HomePager visibility has a race where if the HomePager is shown // (starting the animation), the HomePager is hidden, and the HomePager animation completes, // both the web content and the HomePager will be hidden. This flag is used to prevent the @@ -487,7 +490,9 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() { public void onStopEditing() { - // Re-enable doorhanger notifications. + selectTargetTabForEditingMode(); + + // Re-enable doorhanger notifications. They may trigger on the selected tab above. mDoorHangerPopup.enable(); } }); @@ -1311,7 +1316,10 @@ abstract public class BrowserApp extends GeckoApp if (tabs.isSelectedTabId(tabId)) { hideHomePager(); } else { - tabs.selectTab(tabId); + // Set the target tab to null so it does not get selected (on editing + // mode exit) in lieu of the tab we are about to select. + mTargetTabForEditingMode = null; + Tabs.getInstance().selectTab(tabId); } hideBrowserSearch(); @@ -1425,6 +1433,9 @@ abstract public class BrowserApp extends GeckoApp throw new IllegalArgumentException("Cannot handle null URLs in enterEditingMode"); } + final Tab selectedTab = Tabs.getInstance().getSelectedTab(); + mTargetTabForEditingMode = (selectedTab != null ? selectedTab.getId() : null); + final PropertyAnimator animator = new PropertyAnimator(250); animator.setUseHardwareLayer(false); @@ -1537,6 +1548,22 @@ abstract public class BrowserApp extends GeckoApp } } + /** + * Selects the target tab for editing mode. This is expected to be the tab selected on editing + * mode entry, unless it is subsequently overridden. + * + * A background tab may be selected while editing mode is active (e.g. popups), causing the + * new url to load in the newly selected tab. Call this method on editing mode exit to + * mitigate this. + */ + private void selectTargetTabForEditingMode() { + if (mTargetTabForEditingMode != null) { + Tabs.getInstance().selectTab(mTargetTabForEditingMode); + } + + mTargetTabForEditingMode = null; + } + /** * Shows or hides the home pager for the given tab. */ From 14d37a7b268902bf51e6322ae85695330410bd02 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:20:26 -0700 Subject: [PATCH 08/21] Bug 915918 - Part 1.5: Refactor hiding of BrowserSearch and HomePager into onStopEditing. r=lucasr --- mobile/android/base/BrowserApp.java | 31 +++++++++++------------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index de313f86922..1c1bb03c018 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -491,6 +491,8 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() { public void onStopEditing() { selectTargetTabForEditingMode(); + hideHomePager(); + hideBrowserSearch(); // Re-enable doorhanger notifications. They may trigger on the selected tab above. mDoorHangerPopup.enable(); @@ -1312,17 +1314,11 @@ abstract public class BrowserApp extends GeckoApp return false; } - // If this tab is already selected, just hide the home pager. - if (tabs.isSelectedTabId(tabId)) { - hideHomePager(); - } else { - // Set the target tab to null so it does not get selected (on editing - // mode exit) in lieu of the tab we are about to select. - mTargetTabForEditingMode = null; - Tabs.getInstance().selectTab(tabId); - } + // Set the target tab to null so it does not get selected (on editing + // mode exit) in lieu of the tab we are about to select. + mTargetTabForEditingMode = null; + Tabs.getInstance().selectTab(tabId); - hideBrowserSearch(); mBrowserToolbar.cancelEdit(); return true; @@ -1350,7 +1346,6 @@ abstract public class BrowserApp extends GeckoApp Tabs.getInstance().loadUrl(url, searchEngine, -1, flags); - hideBrowserSearch(); mBrowserToolbar.cancelEdit(); } @@ -1451,8 +1446,6 @@ abstract public class BrowserApp extends GeckoApp } final String url = mBrowserToolbar.commitEdit(); - hideHomePager(); - hideBrowserSearch(); // Don't do anything if the user entered an empty URL. if (TextUtils.isEmpty(url)) { @@ -1526,13 +1519,13 @@ abstract public class BrowserApp extends GeckoApp return false; } - mBrowserToolbar.cancelEdit(); - - // Resetting the visibility of HomePager, which might have been hidden - // by the filterEditingMode(). + // cancelEdit will call hideHomePager. If we're on web content, this is fine. If we're on + // about:home, the HomePager needs to be visible in the end (note that hideHomePager will + // not hide the HomePager on about:home). However, filterEditingMode may have hidden the + // HomePager so we set it visible here. mHomePager.setVisibility(View.VISIBLE); - hideHomePager(); - hideBrowserSearch(); + + mBrowserToolbar.cancelEdit(); return true; } From ab0db3d35940dd3212294e1677efe77f871acded Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:20:26 -0700 Subject: [PATCH 09/21] Bug 915918 - Part 2: openUrl -> openUrlAndExitEditingMode. r=lucasr --- mobile/android/base/BrowserApp.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 1c1bb03c018..23578fecb66 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1324,19 +1324,19 @@ abstract public class BrowserApp extends GeckoApp return true; } - private void openUrl(String url) { - openUrl(url, null, false); + private void openUrlAndStopEditing(String url) { + openUrlAndStopEditing(url, null, false); } - private void openUrl(String url, boolean newTab) { - openUrl(url, null, newTab); + private void openUrlAndStopEditing(String url, boolean newTab) { + openUrlAndStopEditing(url, null, newTab); } - private void openUrl(String url, String searchEngine) { - openUrl(url, searchEngine, false); + private void openUrlAndStopEditing(String url, String searchEngine) { + openUrlAndStopEditing(url, searchEngine, false); } - private void openUrl(String url, String searchEngine, boolean newTab) { + private void openUrlAndStopEditing(String url, String searchEngine, boolean newTab) { mBrowserToolbar.setProgressVisibility(true); int flags = Tabs.LOADURL_NONE; @@ -2356,7 +2356,7 @@ abstract public class BrowserApp extends GeckoApp for (String url : urls) { if (!maybeSwitchToTab(url, flags)) { - openUrl(url, true); + openUrlAndStopEditing(url, true); } } } @@ -2365,7 +2365,7 @@ abstract public class BrowserApp extends GeckoApp @Override public void onUrlOpen(String url, EnumSet flags) { if (!maybeSwitchToTab(url, flags)) { - openUrl(url); + openUrlAndStopEditing(url); } } @@ -2373,7 +2373,7 @@ abstract public class BrowserApp extends GeckoApp @Override public void onSearch(String engineId, String text) { recordSearch(engineId, "barsuggest"); - openUrl(text, engineId); + openUrlAndStopEditing(text, engineId); } // BrowserSearch.OnEditSuggestionListener From 266ad648b941d3ba3d501d5e30b38375099b78be Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:41:42 -0700 Subject: [PATCH 10/21] Backed out changeset c9bdbfd50135 (bug 915918) --- mobile/android/base/BrowserApp.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 23578fecb66..1c1bb03c018 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1324,19 +1324,19 @@ abstract public class BrowserApp extends GeckoApp return true; } - private void openUrlAndStopEditing(String url) { - openUrlAndStopEditing(url, null, false); + private void openUrl(String url) { + openUrl(url, null, false); } - private void openUrlAndStopEditing(String url, boolean newTab) { - openUrlAndStopEditing(url, null, newTab); + private void openUrl(String url, boolean newTab) { + openUrl(url, null, newTab); } - private void openUrlAndStopEditing(String url, String searchEngine) { - openUrlAndStopEditing(url, searchEngine, false); + private void openUrl(String url, String searchEngine) { + openUrl(url, searchEngine, false); } - private void openUrlAndStopEditing(String url, String searchEngine, boolean newTab) { + private void openUrl(String url, String searchEngine, boolean newTab) { mBrowserToolbar.setProgressVisibility(true); int flags = Tabs.LOADURL_NONE; @@ -2356,7 +2356,7 @@ abstract public class BrowserApp extends GeckoApp for (String url : urls) { if (!maybeSwitchToTab(url, flags)) { - openUrlAndStopEditing(url, true); + openUrl(url, true); } } } @@ -2365,7 +2365,7 @@ abstract public class BrowserApp extends GeckoApp @Override public void onUrlOpen(String url, EnumSet flags) { if (!maybeSwitchToTab(url, flags)) { - openUrlAndStopEditing(url); + openUrl(url); } } @@ -2373,7 +2373,7 @@ abstract public class BrowserApp extends GeckoApp @Override public void onSearch(String engineId, String text) { recordSearch(engineId, "barsuggest"); - openUrlAndStopEditing(text, engineId); + openUrl(text, engineId); } // BrowserSearch.OnEditSuggestionListener From b7e31aef18cc6c26d05dbb4d06d09c8a2d02682e Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:41:42 -0700 Subject: [PATCH 11/21] Backed out changeset d93a9e884f13 (bug 915918) --- mobile/android/base/BrowserApp.java | 31 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 1c1bb03c018..de313f86922 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -491,8 +491,6 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() { public void onStopEditing() { selectTargetTabForEditingMode(); - hideHomePager(); - hideBrowserSearch(); // Re-enable doorhanger notifications. They may trigger on the selected tab above. mDoorHangerPopup.enable(); @@ -1314,11 +1312,17 @@ abstract public class BrowserApp extends GeckoApp return false; } - // Set the target tab to null so it does not get selected (on editing - // mode exit) in lieu of the tab we are about to select. - mTargetTabForEditingMode = null; - Tabs.getInstance().selectTab(tabId); + // If this tab is already selected, just hide the home pager. + if (tabs.isSelectedTabId(tabId)) { + hideHomePager(); + } else { + // Set the target tab to null so it does not get selected (on editing + // mode exit) in lieu of the tab we are about to select. + mTargetTabForEditingMode = null; + Tabs.getInstance().selectTab(tabId); + } + hideBrowserSearch(); mBrowserToolbar.cancelEdit(); return true; @@ -1346,6 +1350,7 @@ abstract public class BrowserApp extends GeckoApp Tabs.getInstance().loadUrl(url, searchEngine, -1, flags); + hideBrowserSearch(); mBrowserToolbar.cancelEdit(); } @@ -1446,6 +1451,8 @@ abstract public class BrowserApp extends GeckoApp } final String url = mBrowserToolbar.commitEdit(); + hideHomePager(); + hideBrowserSearch(); // Don't do anything if the user entered an empty URL. if (TextUtils.isEmpty(url)) { @@ -1519,14 +1526,14 @@ abstract public class BrowserApp extends GeckoApp return false; } - // cancelEdit will call hideHomePager. If we're on web content, this is fine. If we're on - // about:home, the HomePager needs to be visible in the end (note that hideHomePager will - // not hide the HomePager on about:home). However, filterEditingMode may have hidden the - // HomePager so we set it visible here. - mHomePager.setVisibility(View.VISIBLE); - mBrowserToolbar.cancelEdit(); + // Resetting the visibility of HomePager, which might have been hidden + // by the filterEditingMode(). + mHomePager.setVisibility(View.VISIBLE); + hideHomePager(); + hideBrowserSearch(); + return true; } From c2e3ee83b975bba8bf01e7711daad8b9fb59d8dd Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:41:43 -0700 Subject: [PATCH 12/21] Backed out changeset 906461e491d9 (bug 915918) --- mobile/android/base/BrowserApp.java | 31 ++--------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index de313f86922..a81c8fa11e8 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -167,9 +167,6 @@ abstract public class BrowserApp extends GeckoApp private BrowserHealthReporter mBrowserHealthReporter; - // The tab to be selected on editing mode exit. - private Integer mTargetTabForEditingMode = null; - // The animator used to toggle HomePager visibility has a race where if the HomePager is shown // (starting the animation), the HomePager is hidden, and the HomePager animation completes, // both the web content and the HomePager will be hidden. This flag is used to prevent the @@ -490,9 +487,7 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() { public void onStopEditing() { - selectTargetTabForEditingMode(); - - // Re-enable doorhanger notifications. They may trigger on the selected tab above. + // Re-enable doorhanger notifications. mDoorHangerPopup.enable(); } }); @@ -1316,10 +1311,7 @@ abstract public class BrowserApp extends GeckoApp if (tabs.isSelectedTabId(tabId)) { hideHomePager(); } else { - // Set the target tab to null so it does not get selected (on editing - // mode exit) in lieu of the tab we are about to select. - mTargetTabForEditingMode = null; - Tabs.getInstance().selectTab(tabId); + tabs.selectTab(tabId); } hideBrowserSearch(); @@ -1433,9 +1425,6 @@ abstract public class BrowserApp extends GeckoApp throw new IllegalArgumentException("Cannot handle null URLs in enterEditingMode"); } - final Tab selectedTab = Tabs.getInstance().getSelectedTab(); - mTargetTabForEditingMode = (selectedTab != null ? selectedTab.getId() : null); - final PropertyAnimator animator = new PropertyAnimator(250); animator.setUseHardwareLayer(false); @@ -1548,22 +1537,6 @@ abstract public class BrowserApp extends GeckoApp } } - /** - * Selects the target tab for editing mode. This is expected to be the tab selected on editing - * mode entry, unless it is subsequently overridden. - * - * A background tab may be selected while editing mode is active (e.g. popups), causing the - * new url to load in the newly selected tab. Call this method on editing mode exit to - * mitigate this. - */ - private void selectTargetTabForEditingMode() { - if (mTargetTabForEditingMode != null) { - Tabs.getInstance().selectTab(mTargetTabForEditingMode); - } - - mTargetTabForEditingMode = null; - } - /** * Shows or hides the home pager for the given tab. */ From 84dd9d53f2a3cf3a3f9ec9d35276758ede6f8d4f Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:44:10 -0700 Subject: [PATCH 13/21] Backed out changeset 29fb758db402 (bug 924480) --- mobile/android/base/BrowserApp.java | 43 ----------------------------- 1 file changed, 43 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index a81c8fa11e8..cb9183e68bf 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -167,12 +167,6 @@ abstract public class BrowserApp extends GeckoApp private BrowserHealthReporter mBrowserHealthReporter; - // The animator used to toggle HomePager visibility has a race where if the HomePager is shown - // (starting the animation), the HomePager is hidden, and the HomePager animation completes, - // both the web content and the HomePager will be hidden. This flag is used to prevent the - // race by determining if the web content should be hidden at the animation's end. - private boolean mHideWebContentOnAnimationEnd = false; - private SiteIdentityPopup mSiteIdentityPopup; public SiteIdentityPopup getSiteIdentityPopup() { @@ -1580,38 +1574,7 @@ abstract public class BrowserApp extends GeckoApp final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub); mHomePager = (HomePager) homePagerStub.inflate(); } - mHomePager.show(getSupportFragmentManager(), page, animator); - - // Hide the web content so it cannot be focused by screen readers. - hideWebContentOnPropertyAnimationEnd(animator); - } - - private void hideWebContentOnPropertyAnimationEnd(final PropertyAnimator animator) { - if (animator == null) { - hideWebContent(); - return; - } - - animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { - @Override - public void onPropertyAnimationStart() { - mHideWebContentOnAnimationEnd = true; - } - - @Override - public void onPropertyAnimationEnd() { - if (mHideWebContentOnAnimationEnd) { - hideWebContent(); - } - } - }); - } - - private void hideWebContent() { - // The view is set to INVISIBLE, rather than GONE, to avoid - // the additional requestLayout() call. - mLayerView.setVisibility(View.INVISIBLE); } private void hideHomePager() { @@ -1624,12 +1587,6 @@ abstract public class BrowserApp extends GeckoApp return; } - // Prevent race in hiding web content - see declaration for more info. - mHideWebContentOnAnimationEnd = false; - - // Display the previously hidden web content (which prevented screen reader access). - mLayerView.setVisibility(View.VISIBLE); - if (mHomePager != null) { mHomePager.hide(); } From c06e95ba9c01c3133d9eeb87532ad04b6d535b84 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:44:12 -0700 Subject: [PATCH 14/21] Backed out changeset 1c70214e37d0 (bug 924480) --- mobile/android/base/home/HomePager.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 52512320de4..d07debd36d5 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -96,13 +96,6 @@ public class HomePager extends ViewPager { // This is to keep all 4 pages in memory after they are // selected in the pager. setOffscreenPageLimit(3); - - // We can call HomePager.requestFocus to steal focus from the URL bar and drop the soft - // keyboard. However, if there are no focusable views (e.g. an empty reading list), the - // URL bar will be refocused. Therefore, we make the HomePager container focusable to - // ensure there is always a focusable view. This would ordinarily be done via an XML - // attribute, but it is not working properly. - setFocusableInTouchMode(true); } @Override @@ -331,7 +324,10 @@ public class HomePager extends ViewPager { @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - // Drop the soft keyboard by stealing focus from the URL bar. + // XXX: Drop the soft keyboard by stealing focus. Note that the HomePager (via XML + // attr) is focusable after its descendants allowing requestFocus to succeed and drop + // the soft keyboard even if there are no other focusable views on the screen (e.g. + // the Reading List is empty). requestFocus(); } From 223eda74099c9394d32496137288cf826eda7459 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 28 Oct 2013 12:44:13 -0700 Subject: [PATCH 15/21] Backed out changeset 36eded3813d6 (bug 924480) --- mobile/android/base/BrowserToolbar.java | 1 - .../base/resources/layout/gecko_app.xml | 21 +++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index f420da4a4ba..b1c88879b6c 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -410,7 +410,6 @@ public class BrowserToolbar extends GeckoRelativeLayout } if (keyCode == KeyEvent.KEYCODE_BACK) { - // Drop the virtual keyboard. clearFocus(); return true; } diff --git a/mobile/android/base/resources/layout/gecko_app.xml b/mobile/android/base/resources/layout/gecko_app.xml index 6b9ca9d8df2..8d3407933c2 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml +++ b/mobile/android/base/resources/layout/gecko_app.xml @@ -55,25 +55,20 @@ android:layout_alignParentBottom="true"> - - - - + + Date: Mon, 28 Oct 2013 12:15:13 -0700 Subject: [PATCH 16/21] Bug 931876 - Rev UUID for SessionStore.idl. r=mfinkle --HG-- extra : rebase_source : 1d6bdc60c6a6769a0518ac195807f71c699c8e06 --- mobile/android/components/SessionStore.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/components/SessionStore.idl b/mobile/android/components/SessionStore.idl index cbc77d176c2..efc51e424b3 100644 --- a/mobile/android/components/SessionStore.idl +++ b/mobile/android/components/SessionStore.idl @@ -14,7 +14,7 @@ interface nsIDOMNode; * tabs contained in them. */ -[scriptable, uuid(15152edf-6c99-4277-9020-076be4653c69)] +[scriptable, uuid(fe116b56-0226-4562-b52a-a623dad07ead)] interface nsISessionStore : nsISupports { /** From 82b3f0e931efbcc61b7cb4f7df0decc0a92f126f Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 22 Oct 2013 03:25:46 +0200 Subject: [PATCH 17/21] Bug 920481 - Call BrowserTabActor.reload when the app is updated to refresh the app. r=paul --- .../devtools/app-manager/content/projects.js | 30 ++++++++++++------- toolkit/devtools/apps/app-actor-front.js | 21 +++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/browser/devtools/app-manager/content/projects.js b/browser/devtools/app-manager/content/projects.js index a0adc6fc128..1ccbc4caded 100644 --- a/browser/devtools/app-manager/content/projects.js +++ b/browser/devtools/app-manager/content/projects.js @@ -14,7 +14,7 @@ const {AppProjects} = require("devtools/app-manager/app-projects"); const {AppValidator} = require("devtools/app-manager/app-validator"); const {Services} = Cu.import("resource://gre/modules/Services.jsm"); const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm"); -const {installHosted, installPackaged, getTargetForApp} = require("devtools/app-actor-front"); +const {installHosted, installPackaged, getTargetForApp, reloadApp} = require("devtools/app-actor-front"); const {EventEmitter} = Cu.import("resource:///modules/devtools/shared/event-emitter.js"); const promise = require("sdk/core/promise"); @@ -176,16 +176,26 @@ let UI = { return this.install(project); } }) - .then( - () => { + .then(() => { button.disabled = false; - }, - (res) => { - button.disabled = false; - let message = res.error + ": " + res.message; - alert(message); - this.connection.log(message); - }); + // Finally try to reload the app if it is already opened + this.reload(project); + }, + (res) => { + button.disabled = false; + let message = res.error + ": " + res.message; + alert(message); + this.connection.log(message); + }); + }, + + reload: function (project) { + return reloadApp(this.connection.client, + this.listTabsResponse.webappsActor, + this._getProjectManifestURL(project)). + then(() => { + this.connection.log("App reloaded"); + }); }, remove: function(location, event) { diff --git a/toolkit/devtools/apps/app-actor-front.js b/toolkit/devtools/apps/app-actor-front.js index 2024a6c3f1e..b6005c2ef0c 100644 --- a/toolkit/devtools/apps/app-actor-front.js +++ b/toolkit/devtools/apps/app-actor-front.js @@ -253,3 +253,24 @@ function getTargetForApp(client, webappsActor, manifestURL) { } exports.getTargetForApp = getTargetForApp; +function reloadApp(client, webappsActor, manifestURL) { + let deferred = promise.defer(); + getTargetForApp(client, + webappsActor, + manifestURL). + then((target) => { + // Request the ContentAppActor to reload the app + let request = { + to: target.form.actor, + type: "reload", + manifestURL: manifestURL + }; + client.request(request, (res) => { + deferred.resolve(); + }); + }, () => { + deferred.reject("Not running"); + }); + return deferred.promise; +} +exports.reloadApp = reloadApp; From bc84fb8110c2c9be63a7682a85eb58c500bf5745 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 22 Oct 2013 03:25:50 +0200 Subject: [PATCH 18/21] Bug 920481 - Ensure flushing jar cache when updating an app. r=paul --- toolkit/devtools/server/actors/webapps.js | 35 ++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/toolkit/devtools/server/actors/webapps.js b/toolkit/devtools/server/actors/webapps.js index a0f64035433..ee2f75907b6 100644 --- a/toolkit/devtools/server/actors/webapps.js +++ b/toolkit/devtools/server/actors/webapps.js @@ -418,12 +418,41 @@ WebappsActor.prototype = { zipFile.moveTo(installDir, "application.zip"); let origin = "app://" + id; + let manifestURL = origin + "/manifest.webapp"; + + // Refresh application.zip content (e.g. reinstall app), as done here: + // http://hg.mozilla.org/mozilla-central/annotate/aaefec5d34f8/dom/apps/src/Webapps.jsm#l1125 + // Do it in parent process for the simulator + let jar = installDir.clone(); + jar.append("application.zip"); + Services.obs.notifyObservers(jar, "flush-cache-entry", null); + + // And then in app content process + // This function will be evaluated in the scope of the content process + // frame script. That will flush the jar cache for this app and allow + // loading fresh updated resources if we reload its document. + let FlushFrameScript = function (path) { + let jar = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsILocalFile); + jar.initWithPath(path); + let obs = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + obs.notifyObservers(jar, "flush-cache-entry", null); + }; + for each (let frame in self._appFrames()) { + if (frame.getAttribute("mozapp") == manifestURL) { + let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; + mm.loadFrameScript("data:," + + encodeURIComponent("(" + FlushFrameScript.toString() + ")" + + "('" + jar.path + "')"), false); + } + } // Create a fake app object with the minimum set of properties we need. let app = { origin: origin, installOrigin: origin, - manifestURL: origin + "/manifest.webapp", + manifestURL: manifestURL, appStatus: appType, receipts: aReceipts, } @@ -739,6 +768,10 @@ WebappsActor.prototype = { }, _appFrames: function () { + // For now, we only support app frames on b2g + if (Services.appinfo.ID != "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}") { + return; + } // Register the system app let chromeWindow = Services.wm.getMostRecentWindow('navigator:browser'); let systemAppFrame = chromeWindow.shell.contentBrowser; From 44cecf6038ba5d8a45f16eb6d6e208b3799bcd31 Mon Sep 17 00:00:00 2001 From: Rob Campbell Date: Tue, 22 Oct 2013 03:25:54 +0200 Subject: [PATCH 19/21] Bug 909756 - Turn off CSS warnings by default in Web Console. r=msucan --- browser/app/profile/firefox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 7d9d028ff1f..1a5baa47d23 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1186,7 +1186,7 @@ pref("devtools.webconsole.filter.network", true); pref("devtools.webconsole.filter.networkinfo", true); pref("devtools.webconsole.filter.netwarn", true); pref("devtools.webconsole.filter.csserror", true); -pref("devtools.webconsole.filter.cssparser", true); +pref("devtools.webconsole.filter.cssparser", false); pref("devtools.webconsole.filter.csslog", false); pref("devtools.webconsole.filter.exception", true); pref("devtools.webconsole.filter.jswarn", true); From acee899306fd1d6e90f50277cf15a904f840c87a Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Mon, 28 Oct 2013 13:58:51 -0700 Subject: [PATCH 20/21] Bug 924968 - Do an update or insert instead of multiple operations when pinning site. r=rnewman --HG-- extra : rebase_source : 1cef6489007b165f09b29b862b38ec532c57fd2e --- mobile/android/base/db/LocalBrowserDB.java | 32 ++++++++-------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/mobile/android/base/db/LocalBrowserDB.java b/mobile/android/base/db/LocalBrowserDB.java index 2fa7a5823f3..1f32f5ac46a 100644 --- a/mobile/android/base/db/LocalBrowserDB.java +++ b/mobile/android/base/db/LocalBrowserDB.java @@ -1161,26 +1161,18 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { values.put(Bookmarks.POSITION, position); values.put(Bookmarks.IS_DELETED, 0); - // If this site is already pinned, unpin it - cr.delete(mBookmarksUriWithProfile, - Bookmarks.PARENT + " == ? AND " + Bookmarks.URL + " == ?", - new String[] { - String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID), - url - }); - - // If something is already pinned in this spot update it - int updated = cr.update(mBookmarksUriWithProfile, - values, - Bookmarks.POSITION + " = ? AND " + - Bookmarks.PARENT + " = ?", - new String[] { Integer.toString(position), - String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) }); - - // Otherwise just insert a new item - if (updated == 0) { - cr.insert(mBookmarksUriWithProfile, values); - } + // We do an update-and-replace here without deleting any existing pins for the given URL. + // That means if the user pins a URL, then edits another thumbnail to use the same URL, + // we'll end up with two pins for that site. This is the intended behavior, which + // incidentally saves us a delete query. + Uri uri = mBookmarksUriWithProfile.buildUpon() + .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(); + cr.update(uri, + values, + Bookmarks.POSITION + " = ? AND " + + Bookmarks.PARENT + " = ?", + new String[] { Integer.toString(position), + String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) }); } @Override From 0103a93247942bc9bbf7653c5bfb3f098c99c0a2 Mon Sep 17 00:00:00 2001 From: Marina Samuel Date: Tue, 29 Oct 2013 00:17:45 -0400 Subject: [PATCH 21/21] Bug 925457: Clicking the urlbar should reset tap coordinates to null so we never decide to shift the browser. r=jmathies --- browser/metro/base/content/browser.xul | 3 ++- .../base/content/contenthandlers/SelectionHandler.js | 6 ++++++ .../metro/base/content/helperui/SelectionHelperUI.js | 10 ++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index 3b520c6acbb..4a474f78418 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -238,7 +238,8 @@ autocompletesearch="history" autocompletepopup="urlbar-autocomplete" completeselectedindex="true" - placeholder="&urlbar.emptytext;"/> + placeholder="&urlbar.emptytext;" + onclick="SelectionHelperUI.urlbarClick();"/> diff --git a/browser/metro/base/content/contenthandlers/SelectionHandler.js b/browser/metro/base/content/contenthandlers/SelectionHandler.js index ba1f545e6fb..c0f6643b9f4 100644 --- a/browser/metro/base/content/contenthandlers/SelectionHandler.js +++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js @@ -25,6 +25,7 @@ var SelectionHandler = { addMessageListener("Browser:SelectionSwitchMode", this); addMessageListener("Browser:RepositionInfoRequest", this); addMessageListener("Browser:SelectionHandlerPing", this); + addMessageListener("Browser:ResetLastPos", this); }, shutdown: function shutdown() { @@ -44,6 +45,7 @@ var SelectionHandler = { removeMessageListener("Browser:SelectionSwitchMode", this); removeMessageListener("Browser:RepositionInfoRequest", this); removeMessageListener("Browser:SelectionHandlerPing", this); + removeMessageListener("Browser:ResetLastPos", this); }, sendAsync: function sendAsync(aMsg, aJson) { @@ -543,6 +545,10 @@ var SelectionHandler = { case "Browser:SelectionHandlerPing": this._onPing(json.id); break; + + case "Browser:ResetLastPos": + this._onClickCoords(json.xPos, json.yPos); + break; } }, diff --git a/browser/metro/base/content/helperui/SelectionHelperUI.js b/browser/metro/base/content/helperui/SelectionHelperUI.js index 147bbdc2655..db759194417 100644 --- a/browser/metro/base/content/helperui/SelectionHelperUI.js +++ b/browser/metro/base/content/helperui/SelectionHelperUI.js @@ -792,6 +792,16 @@ var SelectionHelperUI = { * Event handlers for document events */ + urlbarClick: function() { + // Workaround for bug 925457: taping browser chrome resets last tap + // co-ordinates to 'undefined' so that we know not to shift the browser + // when the keyboard is up (in SelectionHandler._calcNewContentPosition()) + Browser.selectedTab.browser.messageManager.sendAsyncMessage("Browser:ResetLastPos", { + xPos: null, + yPos: null + }); + }, + /* * Handles taps that move the current caret around in text edits, * clear active selection and focus when neccessary, or change