author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Mon, 05 Oct 2015 11:57:59 +0200 | |
changeset 265954 | b56aeea0c4701677ffda6417183caa60d2a6a4a7 |
parent 265946 | 0622c833f6dac163da64cbcc7cabfd2ac34018d0 (current diff) |
parent 265953 | 50a192e5482ba82a724d6630df5089ecbd99125c (diff) |
child 265984 | 45f01961ecd0ad9a45067f3e08bfb92539042eeb |
push id | 29475 |
push user | cbook@mozilla.com |
push date | Mon, 05 Oct 2015 09:58:08 +0000 |
treeherder | mozilla-central@b56aeea0c470 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 44.0a1 |
first release with | nightly linux32
b56aeea0c470
/
44.0a1
/
20151005030206
/
files
nightly linux64
b56aeea0c470
/
44.0a1
/
20151005030206
/
files
nightly mac
b56aeea0c470
/
44.0a1
/
20151005030206
/
files
nightly win32
b56aeea0c470
/
44.0a1
/
20151005030206
/
files
nightly win64
b56aeea0c470
/
44.0a1
/
20151005030206
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
44.0a1
/
20151005030206
/
pushlog to previous
nightly linux64
44.0a1
/
20151005030206
/
pushlog to previous
nightly mac
44.0a1
/
20151005030206
/
pushlog to previous
nightly win32
44.0a1
/
20151005030206
/
pushlog to previous
nightly win64
44.0a1
/
20151005030206
/
pushlog to previous
|
--- a/addon-sdk/source/test/jetpack-package.ini +++ b/addon-sdk/source/test/jetpack-package.ini @@ -139,17 +139,17 @@ skip-if = true [test-test-utils-async.js] [test-test-utils-generator.js] [test-test-utils-sync.js] [test-test-utils.js] [test-text-streams.js] [test-timer.js] [test-traceback.js] [test-ui-action-button.js] -skip-if = (os == "linux" || os == "win") && debug +skip-if = debug || asan # Bug 1208727 [test-ui-frame.js] [test-ui-id.js] [test-ui-sidebar-private-browsing.js] [test-ui-sidebar.js] [test-ui-toggle-button.js] [test-ui-toolbar.js] [test-unit-test-finder.js] [test-unit-test.js]
--- a/browser/base/content/abouthome/aboutHome.js +++ b/browser/base/content/abouthome/aboutHome.js @@ -16,17 +16,17 @@ const DEFAULT_SNIPPETS_URLS = [ const SNIPPETS_UPDATE_INTERVAL_MS = 14400000; // 4 hours. // IndexedDB storage constants. const DATABASE_NAME = "abouthome"; const DATABASE_VERSION = 1; const DATABASE_STORAGE = "persistent"; const SNIPPETS_OBJECTSTORE_NAME = "snippets"; -var searchText, findKey; +var searchText; // This global tracks if the page has been set up before, to prevent double inits var gInitialized = false; var gObserver = new MutationObserver(function (mutations) { for (let mutation of mutations) { if (mutation.attributeName == "snippetsVersion") { if (!gInitialized) { ensureSnippetsMapThen(loadSnippets); @@ -50,24 +50,30 @@ window.addEventListener("pageshow", func document.dispatchEvent(event); }); window.addEventListener("pagehide", function() { window.gObserver.disconnect(); window.removeEventListener("resize", fitToWidth); }); -// make Accel+f focus the search box window.addEventListener("keypress", ev => { - // Make Ctrl/Cmd+f focus the search box. - let modifiers = ev.ctrlKey + ev.altKey + ev.shiftKey + ev.metaKey; - if (ev.getModifierState("Accel") && modifiers == 1 && ev.key == findKey) { - searchText.focus(); - ev.preventDefault(); - } + // focus the search-box on keypress + if (document.activeElement.id == "searchText") // unless already focussed + return; + + let modifiers = ev.ctrlKey + ev.altKey + ev.metaKey; + // ignore Ctrl/Cmd/Alt, but not Shift + // also ignore Tab, Insert, PageUp, etc., and Space + if (modifiers != 0 || ev.charCode == 0 || ev.charCode == 32) + return; + + searchText.focus(); + // need to send the first keypress outside the search-box manually to it + searchText.value += ev.key; }); // This object has the same interface as Map and is used to store and retrieve // the snippets data. It is lazily initialized by ensureSnippetsMapThen(), so // be sure its callback returned before trying to use it. var gSnippetsMap; var gSnippetsMapCallbacks = []; @@ -190,17 +196,16 @@ function setupSearch() // The "autofocus" attribute doesn't focus the form element // immediately when the element is first drawn, so the // attribute is also used for styling when the page first loads. searchText = document.getElementById("searchText"); searchText.addEventListener("blur", function searchText_onBlur() { searchText.removeEventListener("blur", searchText_onBlur); searchText.removeAttribute("autofocus"); }); - findKey = searchText.dataset.findkey; if (!gContentSearchController) { gContentSearchController = new ContentSearchUIController(searchText, searchText.parentNode, "abouthome", "homepage"); } }
--- a/browser/base/content/abouthome/aboutHome.xhtml +++ b/browser/base/content/abouthome/aboutHome.xhtml @@ -38,17 +38,17 @@ <div class="spacer"/> <div id="topSection"> <div id="brandLogo"></div> <div id="searchIconAndTextContainer"> <div id="searchIcon"/> <input type="text" name="q" value="" id="searchText" maxlength="256" aria-label="&contentSearchInput.label;" autofocus="autofocus" - dir="auto" data-findkey="&find.commandkey;"/> + dir="auto"/> <input id="searchSubmit" type="button" value="" onclick="onSearchSubmit(event)" aria-label="&contentSearchSubmit.label;"/> </div> <div id="snippetContainer"> <div id="defaultSnippets" hidden="true"> <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span> <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span>
--- a/browser/base/content/test/general/browser_aboutHome.js +++ b/browser/base/content/test/general/browser_aboutHome.js @@ -415,29 +415,30 @@ var gTests = [ searchController.selectedIndex = 1; EventUtils.synthesizeMouseAtCenter(row, {button: 0}, gBrowser.contentWindow); yield loadPromise; ok(input.value == "x", "Input value did not change"); }); } }, { - desc: "Cmd+f should focus the search box in the page", + desc: "Pressing any key should focus the search box in the page, and send the key to it", setup: function () {}, run: Task.async(function* () { let doc = gBrowser.selectedBrowser.contentDocument; let logo = doc.getElementById("brandLogo"); let searchInput = doc.getElementById("searchText"); EventUtils.synthesizeMouseAtCenter(logo, {}); isnot(searchInput, doc.activeElement, "Search input should not be the active element."); - EventUtils.synthesizeKey("f", { accelKey: true }); + EventUtils.synthesizeKey("a", {}); yield promiseWaitForCondition(() => doc.activeElement === searchInput); is(searchInput, doc.activeElement, "Search input should be the active element."); + is(searchInput.value, "a", "Search input should be 'a'."); }) }, { desc: "Cmd+k should focus the search box in the page when the search box in the toolbar is absent", setup: function () { // Remove the search bar from toolbar CustomizableUI.removeWidgetFromArea("search-container"); }, @@ -486,16 +487,35 @@ var gTests = [ yield EventUtils.synthesizeMouseAtCenter(syncButton, {}, gBrowser.contentWindow); let result = yield openPrefsPromise; window.openPreferences = oldOpenPrefs; is(result.pane, "paneSync", "openPreferences should be called with paneSync"); is(result.params.urlParams.entrypoint, "abouthome", "openPreferences should be called with abouthome entrypoint"); }) +}, +{ + desc: "Pressing Space while the Addons button is focussed should activate it", + setup: function () {}, + run: Task.async(function* () { + // Skip this test on Mac, because Space doesn't activate the button there. + if (navigator.platform.indexOf("Mac") == 0) { + return Promise.resolve(); + } + + info("Waiting for about:addons tab to open..."); + let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons"); + let addOnsButton = gBrowser.selectedBrowser.contentDocument.getElementById("addons"); + addOnsButton.focus(); + EventUtils.synthesizeKey(" ", {}); + let tab = yield promiseTabOpened; + is(tab.linkedBrowser.currentURI.spec, "about:addons", "Should have seen the about:addons tab"); + yield BrowserTestUtils.removeTab(tab); + }) } ]; function test() { waitForExplicitFinish(); requestLongerTimeout(2);
--- a/browser/components/loop/content/shared/css/conversation.css +++ b/browser/components/loop/content/shared/css/conversation.css @@ -752,19 +752,17 @@ body[platform="win"] .share-service-drop background-image: url("../img/icons-16x16.svg#add-hover"); } .dropdown-menu-item:hover:active > .icon-add-share-service { background-image: url("../img/icons-16x16.svg#add-active"); } .context-url-view-wrapper { - /* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p, - 0.5rem for padding of .text-chat-entry > p */ - padding: calc(18px - 1px - 0.5rem); + padding: 12px; margin-bottom: 0.5em; background-color: #dbf7ff; } .showing-room-name > .text-chat-entries > .text-chat-scroller > .context-url-view-wrapper { padding-top: 0; } @@ -1177,163 +1175,145 @@ body[platform="win"] .share-service-drop } /* Text chat in styles */ .text-chat-view { background: white; } -.text-chat-box { - flex: 0 0 auto; - max-height: 40px; - min-height: 40px; - width: 100%; -} - .text-chat-entries { overflow: auto; } -.text-chat-entry { +.text-chat-entry, +.text-chat-header { display: flex; - flex-direction: row; - margin-right: .2em; margin-bottom: .5em; - text-align: start; - flex-wrap: nowrap; - justify-content: flex-start; - align-content: stretch; align-items: flex-start; } -html[dir="rtl"] .text-chat-entry { - margin-right: auto; - margin-left: .2em; +.text-chat-entry { + /* aligns paragraph to side where reading starts from */ + text-align: start; +} + +/* Sent text chat entries should be on the right */ +.text-chat-entry.sent { + /* aligns paragraph to right side */ + justify-content: flex-end; + margin-left: 0; + margin-right: 5px; +} + +.text-chat-entry.received { + margin-left: 4px; + margin-right: 0; +} + +html[dir="rtl"] .text-chat-entry.sent { + margin-left: 5px; + margin-right: 0; +} + + +html[dir="rtl"] .text-chat-entry.received { + margin-left: 0; + margin-right: 5px; } /* If you change this entry, check it doesn't affect the "special" text chat entries as well (.speical, .room-name, .context-url-view-wrapper */ .text-chat-entry > p { position: relative; z-index: 10; /* Drop the default margins from the 'p' element. */ margin: 0; - padding: .5rem; + padding: .8rem; /* leave some room for the chat bubble arrow */ - max-width: 80%; - border-width: 1px; - border-style: solid; - border-color: #5cccee; + max-width: 70%; + border-radius: 15px; + border: 1px solid #5cccee; background: #fff; word-wrap: break-word; flex: 0 1 auto; - align-self: auto; -} - -.text-chat-entry.sent > p, -.text-chat-entry.received > p { - background: #fff; + order: 1; } .text-chat-entry.sent > p { - border-radius: 15px; border-bottom-right-radius: 0; } .text-chat-entry.received > p { - border-radius: 15px; border-top-left-radius: 0; + border-color: #d8d8d8; } html[dir="rtl"] .text-chat-entry.sent > p { - border-radius: 15px; border-bottom-left-radius: 0; + border-bottom-right-radius: 15px; } html[dir="rtl"] .text-chat-entry.received > p { - border-radius: 15px; border-top-right-radius: 0; -} - -.text-chat-entry.received > p { - order: 1; -} - -.text-chat-entry.sent > p { - order: 1; -} - -.text-chat-entry.received { - text-align: start; -} - -.text-chat-entry.received > p { - border-color: #d8d8d8; + border-top-left-radius: 15px; } /* Text chat entry timestamp */ .text-chat-entry-timestamp { margin: 0 .5em; color: #aaa; font-style: italic; font-size: .8em; flex: 0 1 auto; align-self: center; } -/* Sent text chat entries should be on the right */ -.text-chat-entry.sent { - justify-content: flex-end; -} - .received > .text-chat-entry-timestamp { order: 2; } -/* Pseudo element used to cover part between chat bubble and chat arrow. */ +/* Pseudo element used to cover part between chat bubble and chat arrow. + dimensions may change for each position */ .text-chat-entry > p:after { position: absolute; background: #fff; content: ""; + /* default dimensions */ + width: 6px; + height: 7px; } .text-chat-entry.sent > p:after { - right: -2px; + right: -1px; bottom: 0; - width: 15px; - height: 9px; - border-top-left-radius: 15px; - border-top-right-radius: 22px; + width: 7px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; } .text-chat-entry.received > p:after { top: 0; - left: -2px; - width: 15px; - height: 9px; - border-bottom-left-radius: 22px; - border-bottom-right-radius: 15px; + left: -1px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; } html[dir="rtl"] .text-chat-entry.sent > p:after { /* Reset */ right: auto; left: -1px; bottom: 0; - width: 15px; - height: 9px; } html[dir="rtl"] .text-chat-entry.received > p:after { /* Reset */ - left: auto; top: 0; right: -1px; - width: 15px; + width: 9px; height: 6px; } /* Text chat entry arrow */ .text-chat-arrow { width: 18px; background-repeat: no-repeat; flex: 0 1 auto; @@ -1369,39 +1349,43 @@ html[dir="rtl"] .text-chat-entry.sent .t } html[dir="rtl"] .text-chat-entry.received .text-chat-arrow { /* Reset margin. */ margin-right: 0; margin-left: -10px; } -.text-chat-entry.special.room-name { +.text-chat-header.special.room-name { color: black; font-weight: bold; text-align: start; background-color: #dbf7ff; margin-bottom: 0; margin-right: 0; } -.text-chat-entry.special.room-name p { +.text-chat-header.special.room-name p { background: #dbf7ff; max-width: 100%; /* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p, 0.5rem for padding of .text-chat-entry > p */ padding: calc(18px - 1px - 0.5rem); } -.text-chat-entry.special > p { +.text-chat-header.special > p { border: none; } .text-chat-box { margin: auto; + flex: 0 0 auto; + max-height: 40px; + min-height: 40px; + width: 100%; } .text-chat-box > form > input { width: 100%; height: 40px; padding: 0 .4rem .4rem; font-size: 1.1em; border: 0;
--- a/browser/components/loop/content/shared/js/textChatView.js +++ b/browser/components/loop/content/shared/js/textChatView.js @@ -76,17 +76,17 @@ loop.shared.views.chat = (function(mozL1 mixins: [React.addons.PureRenderMixin], propTypes: { message: React.PropTypes.string.isRequired }, render: function() { return ( - React.createElement("div", {className: "text-chat-entry special room-name"}, + React.createElement("div", {className: "text-chat-header special room-name"}, React.createElement("p", null, mozL10n.get("rooms_welcome_title", {conversationName: this.props.message})) ) ); } }); /** * Manages the text entries in the chat entries view. This is split out from
--- a/browser/components/loop/content/shared/js/textChatView.jsx +++ b/browser/components/loop/content/shared/js/textChatView.jsx @@ -76,17 +76,17 @@ loop.shared.views.chat = (function(mozL1 mixins: [React.addons.PureRenderMixin], propTypes: { message: React.PropTypes.string.isRequired }, render: function() { return ( - <div className="text-chat-entry special room-name"> + <div className="text-chat-header special room-name"> <p>{mozL10n.get("rooms_welcome_title", {conversationName: this.props.message})}</p> </div> ); } }); /** * Manages the text entries in the chat entries view. This is split out from
--- a/browser/components/loop/test/shared/textChatView_test.js +++ b/browser/components/loop/test/shared/textChatView_test.js @@ -575,17 +575,17 @@ describe("loop.shared.views.TextChatView store.updateRoomInfo(new sharedActions.UpdateRoomInfo({ roomName: "A wonderful surprise!", roomUrl: "Fake" })); var node = view.getDOMNode(); expect(node.querySelector(".text-chat-entries")).to.not.eql(null); - var entries = node.querySelectorAll(".text-chat-entry"); + var entries = node.querySelectorAll(".text-chat-header"); expect(entries.length).eql(1); expect(entries[0].classList.contains("special")).eql(true); expect(entries[0].classList.contains("room-name")).eql(true); }); it("should render a special entry for the context url", function() { view = mountTestComponent();
--- a/browser/locales/en-US/chrome/browser/aboutHome.dtd +++ b/browser/locales/en-US/chrome/browser/aboutHome.dtd @@ -32,12 +32,8 @@ <!ENTITY abouthome.preferencesButtonUnix.label "Preferences"> <!ENTITY abouthome.addonsButton.label "Add-ons"> <!-- LOCALIZATION NOTE (abouthome.appsButton2.label): This string should be consistent with the Apps menu item in the Tools menu (webapps.label in browser.dtd) and the Apps toolbar button in Firefox's customization palette (web-apps-button.label in customizableWidgets.properties) --> <!ENTITY abouthome.appsButton2.label "Apps"> <!ENTITY abouthome.downloadsButton.label "Downloads"> <!ENTITY abouthome.syncButton.label "&syncBrand.shortName.label;"> - -<!-- LOCALIZATION NOTE (find.commandkey): This is the key to use in - conjunction with accel (Command on Mac or Ctrl on other platforms) to find --> -<!ENTITY find.commandkey "f">
--- a/mobile/android/chrome/content/aboutLogins.js +++ b/mobile/android/chrome/content/aboutLogins.js @@ -1,16 +1,17 @@ /* 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 Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils; +Cu.import("resource://services-common/utils.js"); /*global: CommonUtils */ Cu.import("resource://gre/modules/Messaging.jsm"); -Cu.import("resource://gre/modules/Services.jsm") +Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); XPCOMUtils.defineLazyGetter(window, "gChromeWin", () => window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShellTreeItem) .rootTreeItem @@ -44,74 +45,129 @@ const LOGIN_VIEWED = 1; const LOGIN_EDITED = 2; const LOGIN_PW_TOGGLED = 3; var Logins = { _logins: [], _filterTimer: null, _selectedLogin: null, - _getLogins: function() { - let logins; + // Load the logins list, displaying interstitial UI (see + // #logins-list-loading-body) while loading. There are careful + // jank-avoiding measures taken in this function; be careful when + // modifying it! + // + // Returns a Promise that resolves to the list of logins, ordered by + // hostname. + _promiseLogins: function() { let contentBody = document.getElementById("content-body"); let emptyBody = document.getElementById("empty-body"); let filterIcon = document.getElementById("filter-button"); - this._toggleListBody(true); - emptyBody.classList.add("hidden"); + let showSpinner = () => { + this._toggleListBody(true); + emptyBody.classList.add("hidden"); + }; + + let getAllLogins = () => { + let logins = []; + try { + TelemetryStopwatch.start("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS"); + logins = Services.logins.getAllLogins(); + TelemetryStopwatch.finish("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS"); + } catch(e) { + // It's likely that the Master Password was not entered; give + // a hint to the next person. + throw new Error("Possible Master Password permissions error: " + e.toString()); + } - try { - TelemetryStopwatch.start("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS"); - logins = Services.logins.getAllLogins(); - TelemetryStopwatch.finish("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS"); - } catch(e) { - // Master password was not entered - debug("Master password permissions error: " + e); - logins = []; - } - this._toggleListBody(false); + logins.sort((a, b) => a.hostname.localeCompare(b.hostname)); + + return logins; + }; + + let hideSpinner = (logins) => { + this._toggleListBody(false); + + if (!logins.length) { + contentBody.classList.add("hidden"); + filterIcon.classList.add("hidden"); + emptyBody.classList.remove("hidden"); + } else { + contentBody.classList.remove("hidden"); + emptyBody.classList.add("hidden"); + } + + return logins; + }; - if (!logins.length) { - emptyBody.classList.remove("hidden"); + // Return a promise that is resolved after a paint. + let waitForPaint = () => { + // We're changing 'display'. We need to wait for the new value to take + // effect; otherwise, we'll block and never paint a change. Since + // requestAnimationFrame callback is generally triggered *before* any + // style flush and layout, we wait for two animation frames. This + // approach was cribbed from + // https://dxr.mozilla.org/mozilla-central/rev/5abe3c4deab94270440422c850bbeaf512b1f38d/browser/base/content/browser-fullScreen.js?offset=0#469. + return new Promise(function(resolve, reject) { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + resolve(); + }); + }); + }); + }; - filterIcon.classList.add("hidden"); - contentBody.classList.add("hidden"); - } else { - emptyBody.classList.add("hidden"); + // getAllLogins janks the main-thread. We need to paint before that jank; + // by throwing the janky load onto the next tick, we paint the spinner; the + // spinner is CSS animated off-main-thread. + return Promise.resolve() + .then(showSpinner) + .then(waitForPaint) + .then(getAllLogins) + .then(hideSpinner); + }, - filterIcon.classList.remove("hidden"); - } - - logins.sort((a, b) => a.hostname.localeCompare(b.hostname)); - return this._logins = logins; + // Reload the logins list, displaying interstitial UI while loading. + // Update the stored and displayed list upon completion. + _reloadList: function() { + this._promiseLogins() + .then((logins) => { + this._logins = logins; + this._loadList(logins); + }) + .catch((e) => { + // There's no way to recover from errors, sadly. Log and make + // it obvious that something is up. + this._logins = []; + debug("Failed to _reloadList!"); + Cu.reportError(e); + }); }, _toggleListBody: function(isLoading) { let contentBody = document.getElementById("content-body"); let loadingBody = document.getElementById("logins-list-loading-body"); if (isLoading) { contentBody.classList.add("hidden"); loadingBody.classList.remove("hidden"); } else { loadingBody.classList.add("hidden"); contentBody.classList.remove("hidden"); } - }, init: function () { window.addEventListener("popstate", this , false); Services.obs.addObserver(this, "passwordmgr-storage-changed", false); document.getElementById("update-btn").addEventListener("click", this._onSaveEditLogin.bind(this), false); document.getElementById("password-btn").addEventListener("click", this._onPasswordBtn.bind(this), false); - this._loadList(this._getLogins()); - let filterInput = document.getElementById("filter-input"); let filterContainer = document.getElementById("filter-input-container"); filterInput.addEventListener("input", (event) => { // Stop any in-progress filter timer if (this._filterTimer) { clearTimeout(this._filterTimer); this._filterTimer = null; @@ -142,16 +198,18 @@ var Logins = { filterInput.blur(); filterInput.value = ""; this._loadList(this._logins); }, false); this._showList(); this._updatePasswordBtn(true); + + this._reloadList(); }, uninit: function () { Services.obs.removeObserver(this, "passwordmgr-storage-changed"); window.removeEventListener("popstate", this, false); }, _loadList: function (logins) { @@ -434,18 +492,17 @@ var Logins = { break; } } }, observe: function (subject, topic, data) { switch(topic) { case "passwordmgr-storage-changed": { - // Reload logins content. - this._loadList(this._getLogins()); + this._reloadList(); break; } } }, _filter: function(event) { let value = event.target.value.toLowerCase(); let logins = this._logins.filter((login) => {
--- a/mobile/android/tests/browser/chrome/chrome.ini +++ b/mobile/android/tests/browser/chrome/chrome.ini @@ -1,14 +1,15 @@ [DEFAULT] skip-if = os != 'android' support-files = basic_article.html desktopmode_user_agent.sjs devicesearch.xml + head.js session_formdata_sample.html simpleservice.xml video_controls.html video_discovery.html web_channel.html [test_about_logins.html] [test_accounts.html]
new file mode 100644 --- /dev/null +++ b/mobile/android/tests/browser/chrome/head.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function promiseBrowserEvent(browser, eventType) { + return new Promise((resolve) => { + function handle(event) { + // Since we'll be redirecting, don't make assumptions about the given URL and the loaded URL + if (event.target != browser.contentDocument || event.target.location.href == "about:blank") { + info("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href); + return; + } + info("Received event " + eventType + " from browser"); + browser.removeEventListener(eventType, handle, true); + resolve(event); + } + + browser.addEventListener(eventType, handle, true); + info("Now waiting for " + eventType + " event from browser"); + }); +} + +function promiseNotification(topic) { + Cu.import("resource://gre/modules/Services.jsm"); + + return new Promise((resolve, reject) => { + function observe(subject, topic, data) { + info("Received " + topic + " notification from Gecko"); + Services.obs.removeObserver(observe, topic); + resolve(); + } + Services.obs.addObserver(observe, topic, false); + info("Now waiting for " + topic + " notification from Gecko"); + }); +}
--- a/mobile/android/tests/browser/chrome/test_about_logins.html +++ b/mobile/android/tests/browser/chrome/test_about_logins.html @@ -3,21 +3,23 @@ <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1136477 Migrated from Robocop: https://bugzilla.mozilla.org/show_bug.cgi?id=1184186 --> <head> <meta charset="utf-8"> <title>Test for Bug 1136477</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <link rel="stylesheet" type="text/css" href="chrome://global/skin"/> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> - <script type="application/javascript;version=1.7"> + <script type="application/javascript;version=1.8" src="head.js"></script> + <script type="application/javascript;version=1.8"> - "use strict" + "use strict"; const { interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/Services.jsm"); const LOGIN_FIELDS = { hostname: "http://example.org/tests/robocop/robocop_blank_01.html", @@ -26,63 +28,72 @@ Migrated from Robocop: https://bugzilla. username: "username1", password: "password1", usernameField: "", passwordField: "" }; const LoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1", "nsILoginInfo", "init"); - let BrowserApp; - let browser; - - SimpleTest.waitForExplicitFinish(); - function add_login(login) { let newLogin = new LoginInfo(login.hostname, login.formSubmitUrl, login.realmAny, login.username, login.password, login.usernameField, login.passwordField); Services.logins.addLogin(newLogin); } - function password_setup() { + add_task(function* test_passwords_list() { add_login(LOGIN_FIELDS); // Load about:logins. - BrowserApp = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp; - browser = BrowserApp.addTab("about:logins", { selected: true, parentId: BrowserApp.selectedTab.id }).browser; + let BrowserApp = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp; + let browser = BrowserApp.addTab("about:logins", { selected: true, parentId: BrowserApp.selectedTab.id }).browser; + + yield promiseBrowserEvent(browser, "load"); + + let logins_list_parent = browser.contentDocument.getElementById("logins-list").parentNode; - browser.addEventListener("load", function handle(event) { - browser.removeEventListener("load", handle, true); - Services.tm.mainThread.dispatch(test_passwords_list, Ci.nsIThread.DISPATCH_NORMAL); - }, true); - } + let waitForLoginToBeAdded = new Promise((resolve) => { + let observer = new MutationObserver((mutations) => { + for (let mutation of mutations) { + for (let node of mutation.addedNodes) { + if (node.id == 'logins-list') { + info("Received mutation replacing 'logins-list'"); + resolve(); + return; + } + } + } + info("Skipping spurious mutation not replacing 'logins-list'"); + }); + observer.observe(logins_list_parent, { + childList: true, + }); + info("Now waiting for mutation to replace 'logins-list'"); + }); - function test_passwords_list() { - // Test that the (single) entry added in setup is correct. + yield waitForLoginToBeAdded; + let logins_list = browser.contentDocument.getElementById("logins-list"); + // Test that the (single) entry added in setup is correct. let hostname = logins_list.querySelector(".hostname"); is(hostname.textContent, LOGIN_FIELDS.hostname, "hostname is correct"); let username = logins_list.querySelector(".username"); is(username.textContent, LOGIN_FIELDS.username, "username is correct"); // Cleanup: close about:logins, opened in password_setup() BrowserApp.closeTab(BrowserApp.selectedTab); - - SimpleTest.finish(); - } - - password_setup(); + }); </script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136477">Mozilla Bug 1136477</a> <br> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1184186">Migrated from Robocop testAboutLogins</a> <p id="display"></p>
--- a/mobile/android/tests/browser/chrome/test_desktop_useragent.html +++ b/mobile/android/tests/browser/chrome/test_desktop_useragent.html @@ -6,42 +6,25 @@ Migrated from Robocop: https://bugzilla. --> <head> <meta charset="utf-8"> <title>Test for Bug 1157319</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <link rel="stylesheet" type="text/css" href="chrome://global/skin"/> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript;version=1.7"> "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); - function promiseBrowserEvent(browser, eventType) { - return new Promise((resolve) => { - function handle(event) { - // Since we'll be redirecting, don't make assumptions about the given URL and the loaded URL - if (event.target != browser.contentDocument || event.target.location.href == "about:blank") { - info("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href); - return; - } - info("Received event " + eventType + " from browser"); - browser.removeEventListener(eventType, handle, true); - resolve(event); - } - - browser.addEventListener(eventType, handle, true); - info("Now waiting for " + eventType + " event from browser"); - }); - } - // Load a custom sjs script that echos our "User-Agent" header back at us const TestURI = Services.io.newURI("http://mochi.test:8888/chrome/mobile/android/tests/browser/chrome/desktopmode_user_agent.sjs", null, null); add_task(function* test_desktopmode() { let chromeWin = Services.wm.getMostRecentWindow("navigator:browser"); let BrowserApp = chromeWin.BrowserApp; // Add a new 'desktop mode' tab with our test page
--- a/mobile/android/tests/browser/chrome/test_offline_page.html +++ b/mobile/android/tests/browser/chrome/test_offline_page.html @@ -6,44 +6,27 @@ Migrated from Robocop: https://bugzilla. --> <head> <meta charset="utf-8"> <title>Test for Bug 1089190</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <link rel="stylesheet" type="text/css" href="chrome://global/skin"/> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript;version=1.7"> "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/Task.jsm"); - function promiseBrowserEvent(browser, eventType) { - return new Promise((resolve) => { - function handle(event) { - // Since we'll be redirecting, don't make assumptions about the given URL and the loaded URL - if (event.target != browser.contentDocument || event.target.location.href == "about:blank") { - info("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href); - return; - } - info("Received event " + eventType + " from browser"); - browser.removeEventListener(eventType, handle, true); - resolve(event); - } - - browser.addEventListener(eventType, handle, true); - info("Now waiting for " + eventType + " event from browser"); - }); - } - // Provide a helper to yield until we are sure the offline state has changed function promiseOffline(isOffline) { return new Promise((resolve, reject) => { function observe(subject, topic, data) { info("Received topic: " + topic); Services.obs.removeObserver(observe, "network:offline-status-changed"); resolve(); }
--- a/mobile/android/tests/browser/chrome/test_reader_view.html +++ b/mobile/android/tests/browser/chrome/test_reader_view.html @@ -6,50 +6,23 @@ Migrated from Robocop: https://bugzilla. --> <head> <meta charset="utf-8"> <title>Test for Bug 1158885</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <link rel="stylesheet" type="text/css" href="chrome://global/skin"/> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript;version=1.7"> const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); - function promiseBrowserEvent(browser, eventType) { - return new Promise((resolve) => { - function handle(event) { - // Since we'll be redirecting, don't make assumptions about the given URL and the loaded URL - if (event.target != browser.contentDocument || event.target.location.href == "about:blank") { - info("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href); - return; - } - info("Received event " + eventType + " from browser"); - browser.removeEventListener(eventType, handle, true); - resolve(event); - } - - browser.addEventListener(eventType, handle, true); - info("Now waiting for " + eventType + " event from browser"); - }); - } - - function promiseNotification(topic) { - return new Promise((resolve, reject) => { - function observe(subject, topic, data) { - Services.obs.removeObserver(observe, topic); - resolve(); - } - Services.obs.addObserver(observe, topic, false); - }); - } - add_task(function* test_reader_view_visibility() { let gWin = Services.wm.getMostRecentWindow("navigator:browser"); let BrowserApp = gWin.BrowserApp; let url = "http://mochi.test:8888/chrome/mobile/android/tests/browser/chrome/basic_article.html"; let browser = BrowserApp.addTab("about:reader?url=" + url).browser; SimpleTest.registerCleanupFunction(function() {
--- a/mobile/android/tests/browser/chrome/test_session_form_data.html +++ b/mobile/android/tests/browser/chrome/test_session_form_data.html @@ -6,16 +6,17 @@ Migrated from Robocop: https://bugzilla. --> <head> <meta charset="utf-8"> <title>Test for Bug 671993</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <link rel="stylesheet" type="text/css" href="chrome://global/skin"/> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript;version=1.7"> "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -33,35 +34,16 @@ function sleep(wait) { notify: function () { dump("sleep end"); resolve(); }, }, wait, gTimer.TYPE_ONE_SHOT); }); } -function promiseBrowserLoaded(browser, eventType="load") { - return new Promise((resolve, reject) => { - dump("Wait browser event: " + eventType); - - function handle(event) { - if (event.target != browser.contentDocument) { - dump("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href); - return; - } - - browser.removeEventListener(eventType, handle, true); - dump("Browser event received: " + eventType); - resolve(event); - } - - browser.addEventListener(eventType, handle, true); - }); -} - function queryElement(contentWindow, data) { let frame = contentWindow; if (data.hasOwnProperty("frame")) { frame = contentWindow.frames[data.frame]; } let doc = frame.document; @@ -124,17 +106,17 @@ add_task(function* test_formdata() { // Creates a tab, loads a page with some form fields, // modifies their values and closes the tab. function createAndRemoveTab() { return Task.spawn(function () { // Create a new tab. let tab = gBrowserApp.addTab(URL); let browser = tab.browser; - yield promiseBrowserLoaded(browser); + yield promiseBrowserEvent(browser, "load"); // Modify form data. setInputValue(browser, {id: "txt", value: OUTER_VALUE}); setInputValue(browser, {id: "txt", value: INNER_VALUE, frame: 0}); // Remove the tab. gBrowserApp.closeTab(tab); yield sleep(CLOSE_TAB_WAIT); @@ -179,17 +161,17 @@ add_task(function* test_formdata2() { // Creates a tab, loads a page with some form fields, // modifies their values and closes the tab. function createAndRemoveTab() { return Task.spawn(function () { // Create a new tab. let tab = gBrowserApp.addTab(URL); let browser = tab.browser; - yield promiseBrowserLoaded(browser); + yield promiseBrowserEvent(browser, "load"); // Modify form data. setInputValue(browser, {id: "txt", value: OUTER_VALUE}); setInputValue(browser, {id: "txt", value: INNER_VALUE, frame: 0}); // Remove the tab. gBrowserApp.closeTab(tab); yield sleep(CLOSE_TAB_WAIT); @@ -200,17 +182,17 @@ add_task(function* test_formdata2() { let state = ss.getClosedTabs(gChromeWin); let [{formdata}] = state; is(formdata.id.txt, OUTER_VALUE, "outer value is correct"); is(formdata.children[0].id.txt, INNER_VALUE, "inner value is correct"); // Restore the closed tab. let closedTabData = ss.getClosedTabs(gChromeWin)[0]; let browser = ss.undoCloseTab(gChromeWin, closedTabData); - yield promiseBrowserLoaded(browser); + yield promiseBrowserEvent(browser, "load"); // Check the form data. is(getInputValue(browser, {id: "txt"}), OUTER_VALUE, "outer value restored correctly"); is(getInputValue(browser, {id: "txt", frame: 0}), INNER_VALUE, "inner value restored correctly"); // Remove the tab. gBrowserApp.closeTab(gBrowserApp.getTabForBrowser(browser)); });