author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 15 Apr 2015 12:36:46 +0200 | |
changeset 270503 | bee53c88ced0e24090d682fd65b4e15b9283f256 |
parent 270486 | a6f7a33731bc3fe22073129bba83fc7480e387e2 (current diff) |
parent 270502 | 966d5cf2202054c72ad21688d7df1213a32e23dc (diff) |
child 270609 | b58b07945d30ba552b32ba1c872916ca44563f95 |
push id | 863 |
push user | raliiev@mozilla.com |
push date | Mon, 03 Aug 2015 13:22:43 +0000 |
treeherder | mozilla-release@f6321b14228d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 40.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/addon-sdk/source/lib/sdk/ui/button/view.js +++ b/addon-sdk/source/lib/sdk/ui/button/view.js @@ -137,17 +137,17 @@ function create(options) { node.style.display = 'none'; node.setAttribute('id', this.id); node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional badged-button'); node.setAttribute('type', type); node.setAttribute('label', label); node.setAttribute('tooltiptext', label); node.setAttribute('image', image); - node.setAttribute('sdk-button', 'true'); + node.setAttribute('constrain-size', 'true'); views.set(id, { area: this.currentArea, icon: icon, label: label }); node.addEventListener('command', function(event) {
--- a/addon-sdk/source/test/test-sandbox.js +++ b/addon-sdk/source/test/test-sandbox.js @@ -52,17 +52,17 @@ exports['test exceptions'] = function(as let fixture = sandbox(); try { evaluate(fixture, '!' + function() { var message = 'boom'; throw Error(message); } + '();'); } catch (error) { - assert.equal(error.fileName, '', 'no fileName reported'); + assert.equal(error.fileName, '[System Principal]', 'No specific fileName reported'); assert.equal(error.lineNumber, 3, 'reports correct line number'); } try { evaluate(fixture, '!' + function() { var message = 'boom'; throw Error(message); } + '();', 'foo.js');
--- a/browser/base/content/browser-readinglist.js +++ b/browser/base/content/browser-readinglist.js @@ -247,17 +247,17 @@ let ReadingListUI = { // nothing to do if we have no button. return; } let uri; if (this.enabled && state == "valid") { uri = gBrowser.currentURI; if (uri.schemeIs("about")) - uri = ReaderParent.parseReaderUrl(uri.spec); + uri = ReaderMode.getOriginalUrl(uri.spec); else if (!uri.schemeIs("http") && !uri.schemeIs("https")) uri = null; } let msg = {topic: "UpdateActiveItem", url: null}; if (!uri) { this.toolbarButton.setAttribute("hidden", true); if (this.isSidebarOpen) @@ -304,17 +304,17 @@ let ReadingListUI = { * removing otherwise. * * @param {<xul:browser>} browser - Browser with page to toggle. * @returns {Promise} Promise resolved when operation has completed. */ togglePageByBrowser: Task.async(function* (browser) { let uri = browser.currentURI; if (uri.spec.startsWith("about:reader?")) - uri = ReaderParent.parseReaderUrl(uri.spec); + uri = ReaderMode.getOriginalUrl(uri.spec); if (!uri) return; let item = yield ReadingList.itemForURL(uri); if (item) { yield item.delete(); } else { yield ReadingList.addItemFromBrowser(browser, uri); @@ -325,17 +325,17 @@ let ReadingListUI = { * Checks if a given item matches the current tab in this window. * * @param {ReadingListItem} item - Item to check * @returns True if match, false otherwise. */ isItemForCurrentBrowser(item) { let currentURL = gBrowser.currentURI.spec; if (currentURL.startsWith("about:reader?")) - currentURL = ReaderParent.parseReaderUrl(currentURL); + currentURL = ReaderMode.getOriginalUrl(currentURL); if (item.url == currentURL || item.resolvedURL == currentURL) { return true; } return false; }, /**
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -208,16 +208,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource:///modules/UITour.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "CastingApps", "resource:///modules/CastingApps.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", + "resource://gre/modules/ReaderMode.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent", "resource:///modules/ReaderParent.jsm"); let gInitialPages = [ "about:blank", "about:newtab", "about:home", "about:privatebrowsing",
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -573,16 +573,17 @@ nsContextMenu.prototype = { this.onLink = false; this.onMailtoLink = false; this.onSaveableLink = false; this.link = null; this.linkURL = ""; this.linkURI = null; this.linkText = ""; this.linkProtocol = ""; + this.linkDownload = ""; this.linkHasNoReferrer = false; this.onMathML = false; this.inFrame = false; this.inSrcdocFrame = false; this.inSyntheticDoc = false; this.hasBGImage = false; this.bgImageURL = ""; this.onEditableArea = false; @@ -746,16 +747,24 @@ nsContextMenu.prototype = { this.link = elem; this.linkURL = this.getLinkURL(); this.linkURI = this.getLinkURI(); this.linkText = this.getLinkText(); this.linkProtocol = this.getLinkProtocol(); this.onMailtoLink = (this.linkProtocol == "mailto"); this.onSaveableLink = this.isLinkSaveable( this.link ); this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem); + try { + if (elem.download) { + // Ignore download attribute on cross-origin links + this.principal.checkMayLoad(this.linkURI, false, true); + this.linkDownload = elem.download; + } + } + catch (ex) {} } // Background image? Don't bother if we've already found a // background image further down the hierarchy. Otherwise, // we look for the computed background-image style. if (!this.hasBGImage && !this._hasMultipleBGImages) { let bgImgUrl; @@ -1166,17 +1175,18 @@ nsContextMenu.prototype = { // Save URL of clicked-on frame. saveFrame: function () { saveDocument(this.target.ownerDocument); }, // Helper function to wait for appropriate MIME-type headers and // then prompt the user with a file picker - saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc) { + saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc, + linkDownload) { // canonical def in nsURILoader.h const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020; // an object to proxy the data through to // nsIExternalHelperAppService.doContent, which will wait for the // appropriate MIME-type headers and then prompt the user with a // file picker function saveAsListener() {} @@ -1272,16 +1282,18 @@ nsContextMenu.prototype = { var ioService = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); var channel = ioService.newChannelFromURI2(makeURI(linkURL), null, // aLoadingNode this.principal, // aLoadingPrincipal null, // aTriggeringPrincipal Ci.nsILoadInfo.SEC_NORMAL, Ci.nsIContentPolicy.TYPE_OTHER); + if (linkDownload) + channel.contentDispositionFilename = linkDownload; if (channel instanceof Ci.nsIPrivateBrowsingChannel) { let docIsPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser); channel.setPrivate(docIsPrivate); } channel.notificationCallbacks = new callbacks(); let flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS; @@ -1308,17 +1320,18 @@ nsContextMenu.prototype = { // kick off the channel with our proxy object as the listener channel.asyncOpen(new saveAsListener(), null); }, // Save URL of clicked-on link. saveLink: function() { urlSecurityCheck(this.linkURL, this.principal); - this.saveHelper(this.linkURL, this.linkText, null, true, this.ownerDoc); + this.saveHelper(this.linkURL, this.linkText, null, true, this.ownerDoc, + this.linkDownload); }, // Backwards-compatibility wrapper saveImage : function() { if (this.onCanvas || this.onImage) this.saveMedia(); }, @@ -1336,17 +1349,17 @@ nsContextMenu.prototype = { let uri = gContextMenuContentData.documentURIObject; saveImageURL(this.mediaURL, null, "SaveImageTitle", false, false, uri, doc, gContextMenuContentData.contentType, gContextMenuContentData.contentDisposition); } else if (this.onVideo || this.onAudio) { urlSecurityCheck(this.mediaURL, this.principal); var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle"; - this.saveHelper(this.mediaURL, null, dialogTitle, false, doc); + this.saveHelper(this.mediaURL, null, dialogTitle, false, doc, ""); } }, // Backwards-compatibility wrapper sendImage : function() { if (this.onCanvas || this.onImage) this.sendMedia(); },
--- a/browser/base/content/test/general/browser_readerMode.js +++ b/browser/base/content/test/general/browser_readerMode.js @@ -12,17 +12,17 @@ const TEST_PREFS = [ ["browser.readinglist.enabled", true], ["browser.readinglist.introShown", false], ]; const TEST_PATH = "http://example.com/browser/browser/base/content/test/general/"; let readerButton = document.getElementById("reader-mode-button"); -add_task(function* () { +add_task(function* test_reader_button() { registerCleanupFunction(function() { // Reset test prefs. TEST_PREFS.forEach(([name, value]) => { Services.prefs.clearUserPref(name); }); while (gBrowser.tabs.length > 1) { gBrowser.removeCurrentTab(); } @@ -85,8 +85,21 @@ add_task(function* () { yield promiseWaitForCondition(() => readerButton.hidden); is_element_hidden(readerButton, "Reader mode button is not present on a non-reader-able page"); // Switch back to the original tab to make sure reader mode button is still visible. gBrowser.removeCurrentTab(); yield promiseWaitForCondition(() => !readerButton.hidden); is_element_visible(readerButton, "Reader mode button is present on a reader-able page"); }); + +add_task(function* test_getOriginalUrl() { + let { ReaderMode } = Cu.import("resource://gre/modules/ReaderMode.jsm", {}); + let url = "http://foo.com/article.html"; + + is(ReaderMode.getOriginalUrl("about:reader?url=" + encodeURIComponent(url)), url, "Found original URL from encoded URL"); + is(ReaderMode.getOriginalUrl("about:reader?foobar"), null, "Did not find original URL from malformed reader URL"); + is(ReaderMode.getOriginalUrl(url), null, "Did not find original URL from non-reader URL"); + + let badUrl = "http://foo.com/?;$%^^"; + is(ReaderMode.getOriginalUrl("about:reader?url=" + encodeURIComponent(badUrl)), badUrl, "Found original URL from encoded malformed URL"); + is(ReaderMode.getOriginalUrl("about:reader?url=" + badUrl), badUrl, "Found original URL from non-encoded malformed URL"); +});
--- a/browser/base/content/test/newtab/browser_newtab_block.js +++ b/browser/base/content/test/newtab/browser_newtab_block.js @@ -1,17 +1,31 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /* * These tests make sure that blocking/removing sites from the grid works * as expected. Pinned tabs should not be moved. Gaps will be re-filled * if more sites are available. */ + +gDirectorySource = "data:application/json," + JSON.stringify({ + "suggested": [{ + url: "http://suggested.com/", + imageURI: "data:image/png;base64,helloWORLD3", + title: "title", + type: "affiliate", + frecent_sites: ["example0.com"] + }] +}); + function runTests() { + let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; + NewTabUtils.isTopPlacesSite = (site) => false; + // we remove sites and expect the gaps to be filled as long as there still // are some sites available yield setLinks("0,1,2,3,4,5,6,7,8,9"); setPinnedLinks(""); yield addNewTabPageTab(); checkGrid("0,1,2,3,4,5,6,7,8"); @@ -53,9 +67,21 @@ function runTests() { yield setLinks("0,1,2,3,4,5,6,7,8,9"); setPinnedLinks(",,,,,,,,8"); yield addNewTabPageTab(); checkGrid("0,1,2,3,4,5,6,7,8p"); yield blockCell(0); checkGrid("1,2,3,4,5,6,7,9,8p"); + + // Test that blocking the targeted site also removes its associated suggested tile + NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; + yield restore(); + yield setLinks("0,1,2,3,4,5,6,7,8,9"); + yield addNewTabPageTab(); + yield customizeNewTabPage("enhanced"); + checkGrid("http://suggested.com/,0,1,2,3,4,5,6,7,8,9"); + + yield blockCell(1); + yield addNewTabPageTab(); + checkGrid("1,2,3,4,5,6,7,8,9"); }
--- a/browser/base/content/test/newtab/browser_newtab_enhanced.js +++ b/browser/base/content/test/newtab/browser_newtab_enhanced.js @@ -104,27 +104,28 @@ function runTests() { yield unpinCell(0); // Test that a suggested tile is not enhanced by a directory tile let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; NewTabUtils.isTopPlacesSite = () => true; - yield setLinks("-1"); + yield setLinks("-1,2,3,4,5,6,7,8"); // Test with enhanced = false yield addNewTabPageTab(); yield customizeNewTabPage("classic"); ({type, enhanced, title} = getData(0)); isnot(type, "enhanced", "history link is not enhanced"); is(enhanced, "", "history link has no enhanced image"); is(title, "site#-1"); - is(getData(1), null, "there is only one link and it's a history link"); + isnot(getData(7), null, "there are 8 history links"); + is(getData(8), null, "there are 8 history links"); // Test with enhanced = true yield addNewTabPageTab(); yield customizeNewTabPage("enhanced"); // Suggested link was not enhanced by directory link with same domain ({type, enhanced, title} = getData(0)); @@ -133,10 +134,10 @@ function runTests() { is(title, "title2"); // Enhanced history link shows up second ({type, enhanced, title} = getData(1)); is(type, "enhanced", "pinned history link is enhanced"); isnot(enhanced, "", "pinned history link has enhanced image"); is(title, "title"); - is(getData(2), null, "there is only a suggested link followed by an enhanced history link"); + is(getData(9), null, "there is a suggested link followed by an enhanced history link and the remaining history links"); }
--- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -163,17 +163,17 @@ file, You can obtain one at http://mozil } case "keyword": // Fall through. case "searchengine": { returnValue = action.params.input; break; } } } else { - let originalUrl = ReaderParent.parseReaderUrl(aValue); + let originalUrl = ReaderMode.getOriginalUrl(aValue); if (originalUrl) { returnValue = originalUrl; } } // Set the actiontype only if the user is not overriding actions. if (action && this._noActionsKeys.size == 0) { this.setAttribute("actiontype", action.type);
--- a/browser/components/preferences/in-content/content.js +++ b/browser/components/preferences/in-content/content.js @@ -180,26 +180,26 @@ var gContentPane = { }, /** * Displays the fonts dialog, where web page font names and sizes can be * configured. */ configureFonts: function () { - gSubDialog.open("chrome://browser/content/preferences/fonts.xul"); + gSubDialog.open("chrome://browser/content/preferences/fonts.xul", "resizable=no"); }, /** * Displays the colors dialog, where default web page/link/etc. colors can be * configured. */ configureColors: function () { - gSubDialog.open("chrome://browser/content/preferences/colors.xul"); + gSubDialog.open("chrome://browser/content/preferences/colors.xul", "resizable=no"); }, // LANGUAGES /** * Shows a dialog in which the preferred language for web content may be set. */ showLanguages: function ()
--- a/browser/components/preferences/in-content/privacy.js +++ b/browser/components/preferences/in-content/privacy.js @@ -506,17 +506,17 @@ var gPrivacyPane = { * Clear Private Data settings, false otherwise */ /** * Displays the Clear Private Data settings dialog. */ showClearPrivateDataSettings: function () { - gSubDialog.open("chrome://browser/content/preferences/sanitize.xul"); + gSubDialog.open("chrome://browser/content/preferences/sanitize.xul", "resizable=no"); }, /** * Displays a dialog from which individual parts of private data may be * cleared. */ clearPrivateDataNow: function (aClearEverything)
--- a/browser/components/preferences/in-content/security.js +++ b/browser/components/preferences/in-content/security.js @@ -212,17 +212,17 @@ var gSecurityPane = { }, /** * Displays a dialog in which the master password may be changed. */ changeMasterPassword: function () { gSubDialog.open("chrome://mozapps/content/preferences/changemp.xul", - null, null, this._initMasterPasswordUI.bind(this)); + "resizable=no", null, this._initMasterPasswordUI.bind(this)); }, /** * Shows the sites where the user has saved passwords and the associated login * information. */ showPasswords: function () {
--- a/browser/modules/DirectoryLinksProvider.jsm +++ b/browser/modules/DirectoryLinksProvider.jsm @@ -60,16 +60,19 @@ const ALLOWED_IMAGE_SCHEMES = new Set([" const DIRECTORY_FRECENCY = 1000; // The frecency of a suggested link const SUGGESTED_FRECENCY = Infinity; // Default number of times to show a link const DEFAULT_FREQUENCY_CAP = 5; +// The min number of visible (not blocked) history tiles to have before showing suggested tiles +const MIN_VISIBLE_HISTORY_TILES = 8; + // Divide frecency by this amount for pings const PING_SCORE_DIVISOR = 10000; // Allowed ping actions remotely stored as columns: case-insensitive [a-z0-9_] const PING_ACTIONS = ["block", "click", "pin", "sponsored", "sponsored_link", "unpin", "view"]; /** * Singleton that serves as the provider of directory links. @@ -504,16 +507,17 @@ let DirectoryLinksProvider = { init: function DirectoryLinksProvider_init() { this._setDefaultEnhanced(); this._addPrefsObserver(); // setup directory file path and last download timestamp this._directoryFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, DIRECTORY_LINKS_FILE); this._lastDownloadMS = 0; NewTabUtils.placesProvider.addObserver(this); + NewTabUtils.links.addObserver(this); return Task.spawn(function() { // get the last modified time of the links file if it exists let doesFileExists = yield OS.File.exists(this._directoryFilePath); if (doesFileExists) { let fileInfo = yield OS.File.stat(this._directoryFilePath); this._lastDownloadMS = Date.parse(fileInfo.lastModificationDate); } @@ -558,17 +562,17 @@ let DirectoryLinksProvider = { NewTabUtils.links.populateProviderCache(NewTabUtils.placesProvider, () => { this._handleManyLinksChanged(); }); }, onLinkChanged: function (aProvider, aLink) { // Make sure NewTabUtils.links handles the notification first. setTimeout(() => { - if (this._handleLinkChanged(aLink)) { + if (this._handleLinkChanged(aLink) || this._shouldUpdateSuggestedTile()) { this._updateSuggestedTile(); } }, 0); }, onManyLinksChanged: function () { // Make sure NewTabUtils.links handles the notification first. setTimeout(() => { @@ -586,16 +590,48 @@ let DirectoryLinksProvider = { this._frequencyCaps.set(url, remainingViews); // Reached the number of views, so pick a new one. if (remainingViews <= 0) { this._updateSuggestedTile(); } }, + _getCurrentTopSiteCount: function() { + let visibleTopSiteCount = 0; + for (let link of NewTabUtils.links.getLinks().slice(0, MIN_VISIBLE_HISTORY_TILES)) { + if (link && (link.type == "history" || link.type == "enhanced")) { + visibleTopSiteCount++; + } + } + return visibleTopSiteCount; + }, + + _shouldUpdateSuggestedTile: function() { + let sortedLinks = NewTabUtils.getProviderLinks(this); + + let mostFrecentLink = {}; + if (sortedLinks && sortedLinks.length) { + mostFrecentLink = sortedLinks[0] + } + + let currTopSiteCount = this._getCurrentTopSiteCount(); + if ((!mostFrecentLink.targetedSite && currTopSiteCount >= MIN_VISIBLE_HISTORY_TILES) || + (mostFrecentLink.targetedSite && currTopSiteCount < MIN_VISIBLE_HISTORY_TILES)) { + // If mostFrecentLink has a targetedSite then mostFrecentLink is a suggested link. + // If we have enough history links (8+) to show a suggested tile and we are not + // already showing one, then we should update (to *attempt* to add a suggested tile). + // OR if we don't have enough history to show a suggested tile (<8) and we are + // currently showing one, we should update (to remove it). + return true; + } + + return false; + }, + /** * Chooses and returns a suggested tile based on a user's top sites * that we have an available suggested tile for. * * @return the chosen suggested tile, or undefined if there isn't one */ _updateSuggestedTile: function() { let sortedLinks = NewTabUtils.getProviderLinks(this); @@ -615,18 +651,19 @@ let DirectoryLinksProvider = { url: mostFrecentLink.url, frecency: SUGGESTED_FRECENCY, lastVisitDate: mostFrecentLink.lastVisitDate, type: mostFrecentLink.type, }, 0, true); } } - if (this._topSitesWithSuggestedLinks.size == 0) { - // There are no potential suggested links we can show. + if (this._topSitesWithSuggestedLinks.size == 0 || !this._shouldUpdateSuggestedTile()) { + // There are no potential suggested links we can show or not + // enough history for a suggested tile. return; } // Create a flat list of all possible links we can show as suggested. // Note that many top sites may map to the same suggested links, but we only // want to count each suggested link once (based on url), thus possibleLinks is a map // from url to suggestedLink. Thus, each link has an equal chance of being chosen at // random from flattenedLinks if it appears only once.
--- a/browser/modules/ReaderParent.jsm +++ b/browser/modules/ReaderParent.jsm @@ -167,49 +167,27 @@ let ReaderParent = { }, toggleReaderMode: function(event) { let win = event.target.ownerDocument.defaultView; let browser = win.gBrowser.selectedBrowser; let url = browser.currentURI.spec; if (url.startsWith("about:reader")) { - let originalURL = this._getOriginalUrl(url); + let originalURL = ReaderMode.getOriginalUrl(url); if (!originalURL) { Cu.reportError("Error finding original URL for about:reader URL: " + url); } else { win.openUILinkIn(originalURL, "current", {"allowPinnedTabHostChange": true}); } } else { browser.messageManager.sendAsyncMessage("Reader:ParseDocument", { url: url }); } }, - parseReaderUrl: function(url) { - if (!url.startsWith("about:reader?")) { - return null; - } - return this._getOriginalUrl(url); - }, - - /** - * Returns original URL from an about:reader URL. - * - * @param url An about:reader URL. - * @return The original URL for the article, or null if we did not find - * a properly formatted about:reader URL. - */ - _getOriginalUrl: function(url) { - let searchParams = new URLSearchParams(url.substring("about:reader?".length)); - if (!searchParams.has("url")) { - return null; - } - return decodeURIComponent(searchParams.get("url")); - }, - /** * Gets an article for a given URL. This method will download and parse a document. * * @param url The article URL. * @param browser The browser where the article is currently loaded. * @return {Promise} * @resolves JS object representing the article, or null if no article is found. */
--- a/browser/modules/Social.jsm +++ b/browser/modules/Social.jsm @@ -260,28 +260,29 @@ function CreateSocialStatusWidget(aId, a // The widget is only null if we've created then destroyed the widget. // Once we've actually called createWidget the provider will be set to // PROVIDER_API. if (widget && widget.provider == CustomizableUI.PROVIDER_API) return; CustomizableUI.createWidget({ id: aId, - type: 'custom', + type: "custom", removable: true, defaultArea: CustomizableUI.AREA_NAVBAR, onBuild: function(aDocument) { - let node = aDocument.createElement('toolbarbutton'); + let node = aDocument.createElement("toolbarbutton"); node.id = this.id; - node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional social-status-button badged-button'); + node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional social-status-button badged-button"); node.style.listStyleImage = "url(" + (aProvider.icon32URL || aProvider.iconURL) + ")"; node.setAttribute("origin", aProvider.origin); node.setAttribute("label", aProvider.name); node.setAttribute("tooltiptext", aProvider.name); node.setAttribute("oncommand", "SocialStatus.showPopup(this);"); + node.setAttribute("constrain-size", "true"); if (PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView)) node.setAttribute("disabled", "true"); return node; } }); }; @@ -293,24 +294,25 @@ function CreateSocialMarkWidget(aId, aPr // The widget is only null if we've created then destroyed the widget. // Once we've actually called createWidget the provider will be set to // PROVIDER_API. if (widget && widget.provider == CustomizableUI.PROVIDER_API) return; CustomizableUI.createWidget({ id: aId, - type: 'custom', + type: "custom", removable: true, defaultArea: CustomizableUI.AREA_NAVBAR, onBuild: function(aDocument) { - let node = aDocument.createElement('toolbarbutton'); + let node = aDocument.createElement("toolbarbutton"); node.id = this.id; - node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional social-mark-button'); - node.setAttribute('type', "socialmark"); + node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional social-mark-button"); + node.setAttribute("type", "socialmark"); + node.setAttribute("constrain-size", "true"); node.style.listStyleImage = "url(" + (aProvider.unmarkedIcon || aProvider.icon32URL || aProvider.iconURL) + ")"; node.setAttribute("origin", aProvider.origin); let window = aDocument.defaultView; let menuLabel = window.gNavigatorBundle.getFormattedString("social.markpageMenu.label", [aProvider.name]); node.setAttribute("label", menuLabel); node.setAttribute("tooltiptext", menuLabel); node.setAttribute("observes", "Social:PageShareOrMark");
--- a/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js +++ b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js @@ -218,16 +218,52 @@ function run_test() { DirectoryLinksProvider.reset(); Services.prefs.clearUserPref(kLocalePref); Services.prefs.clearUserPref(kSourceUrlPref); Services.prefs.clearUserPref(kPingUrlPref); Services.prefs.clearUserPref(kNewtabEnhancedPref); }); } +add_task(function test_shouldUpdateSuggestedTile() { + let suggestedLink = { + targetedSite: "somesite.com" + }; + + // DirectoryLinksProvider has no suggested tile and no top sites => no need to update + do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 0); + isIdentical(NewTabUtils.getProviderLinks(), []); + do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), false); + + // DirectoryLinksProvider has a suggested tile and no top sites => need to update + let origGetProviderLinks = NewTabUtils.getProviderLinks; + NewTabUtils.getProviderLinks = (provider) => [suggestedLink]; + + do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 0); + isIdentical(NewTabUtils.getProviderLinks(), [suggestedLink]); + do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), true); + + // DirectoryLinksProvider has a suggested tile and 8 top sites => no need to update + let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; + DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; + + do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 8); + isIdentical(NewTabUtils.getProviderLinks(), [suggestedLink]); + do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), false); + + // DirectoryLinksProvider has no suggested tile and 8 top sites => need to update + NewTabUtils.getProviderLinks = origGetProviderLinks; + do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 8); + isIdentical(NewTabUtils.getProviderLinks(), []); + do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), true); + + // Cleanup + DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; +}); + add_task(function test_updateSuggestedTile() { let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"]; // Initial setup let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]}; let dataURI = 'data:application/json,' + JSON.stringify(data); let testObserver = new TestFirstRun(); @@ -241,16 +277,19 @@ add_task(function test_updateSuggestedTi return topSites.indexOf(site) >= 0; } let origGetProviderLinks = NewTabUtils.getProviderLinks; NewTabUtils.getProviderLinks = function(provider) { return links; } + let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; + DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; + do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined); function TestFirstRun() { this.promise = new Promise(resolve => { this.onLinkChanged = (directoryLinksProvider, link) => { links.unshift(link); let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url]; @@ -341,16 +380,17 @@ add_task(function test_updateSuggestedTi DirectoryLinksProvider.addObserver(testObserver); DirectoryLinksProvider.onManyLinksChanged(); yield testObserver.promise; // Cleanup yield promiseCleanDirectoryLinksProvider(); NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; NewTabUtils.getProviderLinks = origGetProviderLinks; + DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; }); add_task(function test_suggestedLinksMap() { let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3, suggestedTile4], "directory": [someOtherSite]}; let dataURI = 'data:application/json,' + JSON.stringify(data); yield promiseSetupDirectoryLinksProvider({linksURL: dataURI}); let links = yield fetchData(); @@ -431,16 +471,19 @@ add_task(function test_topSitesWithSugge NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; NewTabUtils.getProviderLinks = origGetProviderLinks; }); add_task(function test_suggestedAttributes() { let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; NewTabUtils.isTopPlacesSite = () => true; + let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; + DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; + let frecent_sites = ["top.site.com"]; let imageURI = "https://image/"; let title = "the title"; let type = "affiliate"; let url = "http://test.url/"; let data = { suggested: [{ frecent_sites, @@ -473,25 +516,29 @@ add_task(function test_suggestedAttribut do_check_eq(link.imageURI, imageURI); do_check_eq(link.targetedSite, frecent_sites[0]); do_check_eq(link.title, title); do_check_eq(link.type, type); do_check_eq(link.url, url); // Cleanup. NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; + DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; gLinks.removeProvider(DirectoryLinksProvider); DirectoryLinksProvider.removeObserver(gLinks); }); add_task(function test_frequencyCappedSites_views() { Services.prefs.setCharPref(kPingUrlPref, ""); let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; NewTabUtils.isTopPlacesSite = () => true; + let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; + DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; + let testUrl = "http://frequency.capped/link"; let targets = ["top.site.com"]; let data = { suggested: [{ type: "affiliate", frecent_sites: targets, url: testUrl }], @@ -543,26 +590,30 @@ add_task(function test_frequencyCappedSi checkFirstTypeAndLength("affiliate", 2); synthesizeAction("view"); checkFirstTypeAndLength("affiliate", 2); synthesizeAction("view"); checkFirstTypeAndLength("organic", 1); // Cleanup. NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; + DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; gLinks.removeProvider(DirectoryLinksProvider); DirectoryLinksProvider.removeObserver(gLinks); Services.prefs.setCharPref(kPingUrlPref, kPingUrl); }); add_task(function test_frequencyCappedSites_click() { Services.prefs.setCharPref(kPingUrlPref, ""); let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; NewTabUtils.isTopPlacesSite = () => true; + let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; + DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; + let testUrl = "http://frequency.capped/link"; let targets = ["top.site.com"]; let data = { suggested: [{ type: "affiliate", frecent_sites: targets, url: testUrl }], @@ -608,16 +659,17 @@ add_task(function test_frequencyCappedSi checkFirstTypeAndLength("affiliate", 2); synthesizeAction("view"); checkFirstTypeAndLength("affiliate", 2); synthesizeAction("click"); checkFirstTypeAndLength("organic", 1); // Cleanup. NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; + DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; gLinks.removeProvider(DirectoryLinksProvider); DirectoryLinksProvider.removeObserver(gLinks); Services.prefs.setCharPref(kPingUrlPref, kPingUrl); }); add_task(function test_reportSitesAction() { yield DirectoryLinksProvider.init(); let deferred, expectedPath, expectedPost; @@ -1084,16 +1136,18 @@ add_task(function test_DirectoryLinksPro do_check_eq(links.length, 0); // There are no directory links. checkEnhanced("http://example.net", undefined); checkEnhanced("http://example.com", "data:,fresh"); }); add_task(function test_DirectoryLinksProvider_enhancedURIs() { let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; NewTabUtils.isTopPlacesSite = () => true; + let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; + DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; let data = { "suggested": [ {url: "http://example.net", enhancedImageURI: "data:,net1", title:"SuggestedTitle", frecent_sites: ["test.com"]} ], "directory": [ {url: "http://example.net", enhancedImageURI: "data:,net2", title:"DirectoryTitle"} ] @@ -1124,16 +1178,17 @@ add_task(function test_DirectoryLinksPro // Check that the suggested tile with the same URL replaces the directory tile. links = gLinks.getLinks(); do_check_eq(links.length, 1); do_check_eq(links[0].title, "SuggestedTitle"); do_check_eq(links[0].enhancedImageURI, "data:,net1"); // Cleanup. NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; + DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; gLinks.removeProvider(DirectoryLinksProvider); }); add_task(function test_DirectoryLinksProvider_setDefaultEnhanced() { function checkDefault(expected) { Services.prefs.clearUserPref(kNewtabEnhancedPref); do_check_eq(Services.prefs.getBoolPref(kNewtabEnhancedPref), expected); }
--- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -590,22 +590,22 @@ menuitem:not([type]):not(.menuitem-toolt :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container > .toolbarbutton-icon, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon { padding: 3px 7px; } /* Help SDK icons fit: */ -toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, -toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { width: 16px; } -:-moz-any(#TabsToolbar, #nav-bar) toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { +:-moz-any(#TabsToolbar, #nav-bar) toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */ width: 32px; } #nav-bar #PanelUI-menu-button { -moz-padding-start: 7px; -moz-padding-end: 5px; }
--- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -1348,18 +1348,18 @@ toolbar .toolbarbutton-1 > .toolbarbutto } /* Help 16px icons fit: */ toolbar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon { margin: 2px; } /* Help SDK icons fit: */ -toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, -toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { width: 16px; } #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon { opacity: .4; }
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -295,20 +295,20 @@ toolbaritem[cui-areatype="menu-panel"][s /* In order to have button labels constrained appropriately, items inside the toolbarpaletteitem * should have a min-width set so they abide by the width set above (which they do outside of * customize mode because they're in a flexed container) */ toolbarpaletteitem[place="panel"]:not([haswideitem=true]) > .toolbarbutton-1 { min-width: 0.01px; } /* Help SDK buttons fit in. */ -toolbarpaletteitem[place="palette"] > toolbarbutton[sdk-button="true"] > .toolbarbutton-icon, -toolbarpaletteitem[place="palette"] > toolbarbutton[sdk-button="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, -toolbarbutton[sdk-button="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon, -toolbarbutton[sdk-button="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-icon, +toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, +toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon, +toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-container > .toolbarbutton-icon { height: 32px; width: 32px; } .customization-palette .toolbarbutton-1 { -moz-appearance: none; -moz-box-orient: vertical; }
--- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -728,22 +728,22 @@ toolbarbutton[cui-areatype="toolbar"] > #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container, #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon { padding: 3px 7px; } /* Help SDK icons fit: */ -toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, -toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { width: 16px; } -#nav-bar toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { +#nav-bar toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */ width: 32px; } #nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1[type=menu] > .toolbarbutton-text /* hack for add-ons that forcefully display the label */ { -moz-padding-end: 17px; } @@ -1625,23 +1625,16 @@ richlistitem[type~="action"][actiontype= } .share-provider-button > .toolbarbutton-icon { width: 16px; min-height: 16px; max-height: 16px; } -toolbarbutton[type="socialmark"] > .toolbarbutton-icon { - width: auto; - height: auto; - max-width: 32px; - max-height: 24px; -} - /* fixup corners for share panel */ .social-panel > .social-panel-frame { border-radius: inherit; } #social-share-panel { min-height: 100px; min-width: 766px;
--- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -975,18 +975,31 @@ nsExpandedPrincipal::IsOnCSSUnprefixingW // for an expanded principal. (And probably shouldn't be needed.) return false; } void nsExpandedPrincipal::GetScriptLocation(nsACString& aStr) { - // Is that a good idea to list it's principals? aStr.Assign(EXPANDED_PRINCIPAL_SPEC); + aStr.AppendLiteral(" ("); + + for (size_t i = 0; i < mPrincipals.Length(); ++i) { + if (i != 0) { + aStr.AppendLiteral(", "); + } + + nsAutoCString spec; + nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec); + + aStr.Append(spec); + + } + aStr.Append(")"); } #ifdef DEBUG void nsExpandedPrincipal::dumpImpl() { fprintf(stderr, "nsExpandedPrincipal (%p)\n", static_cast<void*>(this)); } #endif
--- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -441,16 +441,19 @@ GLContextEGL::RenewSurface() { return MakeCurrent(true); } void GLContextEGL::ReleaseSurface() { if (mOwnsContext) { mozilla::gl::DestroySurface(mSurface); } + if (mSurface == mSurfaceOverride) { + mSurfaceOverride = EGL_NO_SURFACE; + } mSurface = EGL_NO_SURFACE; } bool GLContextEGL::SetupLookupFunction() { mLookupFunc = (PlatformLookupFunction)sEGLLibrary.mSymbols.fGetProcAddress; return true; @@ -808,16 +811,51 @@ GLContextProviderEGL::CreateForWindow(ns } glContext->MakeCurrent(); glContext->SetIsDoubleBuffered(doubleBuffered); return glContext.forget(); } +#if defined(ANDROID) +EGLSurface +GLContextProviderEGL::CreateEGLSurface(void* aWindow) +{ + if (!sEGLLibrary.EnsureInitialized()) { + MOZ_CRASH("Failed to load EGL library!\n"); + } + + EGLConfig config; + if (!CreateConfig(&config)) { + MOZ_CRASH("Failed to create EGLConfig!\n"); + } + + MOZ_ASSERT(aWindow); + + EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow, 0); + + if (surface == EGL_NO_SURFACE) { + MOZ_CRASH("Failed to create EGLSurface!\n"); + } + + return surface; +} + +void +GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface) +{ + if (!sEGLLibrary.EnsureInitialized()) { + MOZ_CRASH("Failed to load EGL library!\n"); + } + + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); +} +#endif // defined(ANDROID) + already_AddRefed<GLContextEGL> GLContextEGL::CreateEGLPBufferOffscreenContext(const gfxIntSize& size) { EGLConfig config; EGLSurface surface; const EGLint numConfigs = 1; // We only need one. EGLConfig configs[numConfigs];
--- a/gfx/gl/GLContextProviderImpl.h +++ b/gfx/gl/GLContextProviderImpl.h @@ -5,16 +5,19 @@ #ifndef IN_GL_CONTEXT_PROVIDER_H #error GLContextProviderImpl.h must only be included from GLContextProvider.h #endif #ifndef GL_CONTEXT_PROVIDER_NAME #error GL_CONTEXT_PROVIDER_NAME not defined #endif +#if defined(ANDROID) +typedef void* EGLSurface; +#endif // defined(ANDROID) class GL_CONTEXT_PROVIDER_NAME { public: /** * Create a context that renders to the surface of the widget that is * passed in. The context is always created with an RGB pixel format, * with no alpha, depth or stencil. If any of those features are needed, @@ -71,16 +74,21 @@ public: * @param aContext External context which will be wrapped by Gecko GLContext. * @param aSurface External surface which is used for external context. * * @return Wrapping Context to use for rendering */ static already_AddRefed<GLContext> CreateWrappingExisting(void* aContext, void* aSurface); +#if defined(ANDROID) + static EGLSurface CreateEGLSurface(void* aWindow); + static void DestroyEGLSurface(EGLSurface surface); +#endif // defined(ANDROID) + /** * Get a pointer to the global context, creating it if it doesn't exist. */ static GLContext* GetGlobalContext(); /** * Free any resources held by this Context Provider.
--- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -50,16 +50,21 @@ #include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc #include "nsISupportsImpl.h" // for Layer::AddRef, etc #include "nsIWidget.h" // for nsIWidget #include "nsPoint.h" // for nsIntPoint #include "nsRect.h" // for nsIntRect #include "nsRegion.h" // for nsIntRegion, etc #ifdef MOZ_WIDGET_ANDROID #include <android/log.h> +#include "AndroidBridge.h" +#include "opengl/CompositorOGL.h" +#include "GLContextEGL.h" +#include "GLContextProvider.h" +#include "ScopedGLHelpers.h" #endif #include "GeckoProfiler.h" #include "TextRenderer.h" // for TextRenderer class gfxContext; namespace mozilla { namespace layers { @@ -302,16 +307,19 @@ LayerManagerComposite::EndTransaction(Dr // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); nsIntRegion opaque; ApplyOcclusionCulling(mRoot, opaque); Render(); +#ifdef MOZ_WIDGET_ANDROID + RenderToPresentationSurface(); +#endif mGeometryChanged = false; } else { // Modified layer tree mGeometryChanged = true; } mCompositor->ClearTargetContext(); mTarget = nullptr; @@ -764,16 +772,185 @@ LayerManagerComposite::Render() mCompositor->SetFBAcquireFence(mRoot); } mCompositor->GetWidget()->PostRender(this); RecordFrame(); } +#ifdef MOZ_WIDGET_ANDROID +class ScopedCompositorProjMatrix { +public: + ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix): + mCompositor(aCompositor), + mOriginalProjMatrix(mCompositor->GetProjMatrix()) + { + mCompositor->SetProjMatrix(aProjMatrix); + } + + ~ScopedCompositorProjMatrix() + { + mCompositor->SetProjMatrix(mOriginalProjMatrix); + } +private: + CompositorOGL* const mCompositor; + const Matrix4x4 mOriginalProjMatrix; +}; + +class ScopedCompostitorSurfaceSize { +public: + ScopedCompostitorSurfaceSize(CompositorOGL* aCompositor, const gfx::IntSize& aSize) : + mCompositor(aCompositor), + mOriginalSize(mCompositor->GetDestinationSurfaceSize()) + { + mCompositor->SetDestinationSurfaceSize(aSize); + } + ~ScopedCompostitorSurfaceSize() + { + mCompositor->SetDestinationSurfaceSize(mOriginalSize); + } +private: + CompositorOGL* const mCompositor; + const gfx::IntSize mOriginalSize; +}; + +class ScopedCompositorRenderOffset { +public: + ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) : + mCompositor(aCompositor), + mOriginalOffset(mCompositor->GetScreenRenderOffset()) + { + mCompositor->SetScreenRenderOffset(aOffset); + } + ~ScopedCompositorRenderOffset() + { + mCompositor->SetScreenRenderOffset(mOriginalOffset); + } +private: + CompositorOGL* const mCompositor; + const ScreenPoint mOriginalOffset; +}; + +class ScopedContextSurfaceOverride { +public: + ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) : + mContext(aContext) + { + MOZ_ASSERT(aSurface); + mContext->SetEGLSurfaceOverride(aSurface); + mContext->MakeCurrent(true); + } + ~ScopedContextSurfaceOverride() + { + mContext->SetEGLSurfaceOverride(EGL_NO_SURFACE); + mContext->MakeCurrent(true); + } +private: + GLContextEGL* const mContext; +}; + +void +LayerManagerComposite::RenderToPresentationSurface() +{ + if (!AndroidBridge::Bridge()) { + return; + } + + void* window = AndroidBridge::Bridge()->GetPresentationWindow(); + + if (!window) { + return; + } + + EGLSurface surface = AndroidBridge::Bridge()->GetPresentationSurface(); + + if (!surface) { + //create surface; + surface = GLContextProviderEGL::CreateEGLSurface(window); + if (!surface) { + return; + } + + AndroidBridge::Bridge()->SetPresentationSurface(surface); + } + + CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get()); + GLContext* gl = compositor->gl(); + GLContextEGL* egl = GLContextEGL::Cast(gl); + + if (!egl) { + return; + } + + const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window); + + if ((windowSize.width <= 0) || (windowSize.height <= 0)) { + return; + } + + const int actualWidth = windowSize.width; + const int actualHeight = windowSize.height; + + const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize(); + + const int pageWidth = originalSize.width; + const int pageHeight = originalSize.height; + + float scale = 1.0; + + if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) { + const float scaleWidth = (float)actualWidth / (float)pageWidth; + const float scaleHeight = (float)actualHeight / (float)pageHeight; + scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight; + } + + const gfx::IntSize actualSize(actualWidth, actualHeight); + ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize); + + const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0); + ScopedCompositorRenderOffset overrideRenderOffset(compositor, offset); + ScopedContextSurfaceOverride overrideSurface(egl, surface); + + nsIntRegion invalid; + Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight); + Rect rect, actualBounds; + + mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds); + + // Override the projection matrix since the presentation frame buffer + // is probably not the same size as the device frame buffer. The override + // projection matrix also scales the content to fit into the presentation + // frame buffer. + Matrix viewMatrix; + viewMatrix.PreTranslate(-1.0, 1.0); + viewMatrix.PreScale((2.0f * scale) / (float)actualWidth, (2.0f * scale) / (float)actualHeight); + viewMatrix.PreScale(1.0f, -1.0f); + viewMatrix.PreTranslate((int)((float)offset.x / scale), offset.y); + + Matrix4x4 projMatrix = Matrix4x4::From2D(viewMatrix); + + ScopedCompositorProjMatrix overrideProjMatrix(compositor, projMatrix); + + // The Java side of Fennec sets a scissor rect that accounts for + // chrome such as the URL bar. Override that so that the entire frame buffer + // is cleared. + ScopedScissorRect screen(egl, 0, 0, actualWidth, actualHeight); + egl->fClearColor(0.0, 0.0, 0.0, 0.0); + egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT); + + const nsIntRect clipRect = nsIntRect(0, 0, (int)(scale * pageWidth), actualHeight); + RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect)); + RootLayer()->RenderLayer(clipRect); + + mCompositor->EndFrame(); + mCompositor->SetFBAcquireFence(mRoot); +} +#endif + static void SubtractTransformedRegion(nsIntRegion& aRegion, const nsIntRegion& aRegionToSubtract, const Matrix4x4& aTransform) { if (aRegionToSubtract.IsEmpty()) { return; }
--- a/gfx/layers/composite/LayerManagerComposite.h +++ b/gfx/layers/composite/LayerManagerComposite.h @@ -273,16 +273,19 @@ private: nsIntRegion& aScreenRegion, nsIntRegion& aLowPrecisionScreenRegion, const gfx::Matrix4x4& aTransform); /** * Render the current layer tree to the active target. */ void Render(); +#ifdef MOZ_WIDGET_ANDROID + void RenderToPresentationSurface(); +#endif /** * Render debug overlays such as the FPS/FrameCounter above the frame. */ void RenderDebugOverlay(const gfx::Rect& aBounds); RefPtr<CompositingRenderTarget> PushGroupForLayerEffects();
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -796,16 +796,32 @@ CompositorParent::SchedulePauseOnComposi MOZ_ASSERT(CompositorLoop()); CompositorLoop()->PostTask(FROM_HERE, pauseTask); // Wait until the pause has actually been processed by the compositor thread lock.Wait(); } bool +CompositorParent::ScheduleResumeOnCompositorThread() +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + CancelableTask *resumeTask = + NewRunnableMethod(this, &CompositorParent::ResumeComposition); + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(FROM_HERE, resumeTask); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + +bool CompositorParent::ScheduleResumeOnCompositorThread(int width, int height) { MonitorAutoLock lock(mResumeCompositionMonitor); CancelableTask *resumeTask = NewRunnableMethod(this, &CompositorParent::ResumeCompositionAndResize, width, height); MOZ_ASSERT(CompositorLoop()); CompositorLoop()->PostTask(FROM_HERE, resumeTask);
--- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -221,16 +221,17 @@ public: // Can be called from any thread void ScheduleRenderOnCompositorThread(); void SchedulePauseOnCompositorThread(); /** * Returns true if a surface was obtained and the resume succeeded; false * otherwise. */ + bool ScheduleResumeOnCompositorThread(); bool ScheduleResumeOnCompositorThread(int width, int height); virtual void ScheduleComposition(); void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint, bool aScheduleComposite, uint32_t aPaintSequenceNumber, bool aIsRepeatTransaction); /**
--- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -299,16 +299,29 @@ public: * Doing so lets us use gralloc the way it has been designed to be used * (see https://wiki.mozilla.org/Platform/GFX/Gralloc) */ GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit); const gfx::Matrix4x4& GetProjMatrix() const { return mProjMatrix; } + + void SetProjMatrix(const gfx::Matrix4x4& aProjMatrix) { + mProjMatrix = aProjMatrix; + } + + const gfx::IntSize GetDestinationSurfaceSize() const { + return gfx::IntSize (mSurfaceSize.width, mSurfaceSize.height); + } + + const ScreenPoint& GetScreenRenderOffset() const { + return mRenderOffset; + } + private: virtual gfx::IntSize GetWidgetSize() const override { return mWidgetSize; } bool InitializeVR(); void DestroyVR(GLContext *gl);
--- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1502,17 +1502,17 @@ xpc::EvalInSandbox(JSContext* cx, Handle nsIScriptObjectPrincipal* sop = static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(sandbox)); MOZ_ASSERT(sop, "Invalid sandbox passed"); SandboxPrivate* priv = static_cast<SandboxPrivate*>(sop); nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal(); NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); nsAutoCString filenameBuf; - if (!filename.IsVoid()) { + if (!filename.IsVoid() && filename.Length() != 0) { filenameBuf.Assign(filename); } else { // Default to the spec of the principal. nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); lineNo = 1; } // We create a separate cx to do the sandbox evaluation. Scope it.
new file mode 100644 --- /dev/null +++ b/js/xpconnect/tests/unit/test_sandbox_name.js @@ -0,0 +1,28 @@ +"use strict"; + +const { utils: Cu, interfaces: Ci, classes: Cc } = Components; + +/** + * Test that the name of a sandbox contains the name of all principals. + */ +function test_sandbox_name() { + let names = [ + "http://example.com/?" + Math.random(), + "http://example.org/?" + Math.random() + ]; + let sandbox = Cu.Sandbox(names); + let fileName = Cu.evalInSandbox( + "(new Error()).fileName", + sandbox, + "latest" /*js version*/, + ""/*file name*/ + ); + + for (let name of names) { + Assert.ok(fileName.indexOf(name) != -1, `Name ${name} appears in ${fileName}`); + } +}; + +function run_test() { + test_sandbox_name(); +}
--- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -94,16 +94,17 @@ skip-if = os == "android" # native test [test_url.js] [test_URLSearchParams.js] [test_crypto.js] [test_css.js] [test_rtcIdentityProvider.js] [test_sandbox_atob.js] [test_isProxy.js] [test_getObjectPrincipal.js] +[test_sandbox_name.js] [test_watchdog_enable.js] head = head_watchdog.js [test_watchdog_disable.js] head = head_watchdog.js [test_watchdog_toggle.js] head = head_watchdog.js [test_watchdog_default.js] head = head_watchdog.js
--- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -774,16 +774,17 @@ public class BrowserApp extends GeckoApp "CharEncoding:State", "Favicon:CacheLoad", "Feedback:LastUrl", "Feedback:MaybeLater", "Feedback:OpenPlayStore", "Menu:Add", "Menu:Remove", "Reader:Share", + "Sanitize:ClearHistory", "Settings:Show", "Telemetry:Gather", "Updater:Launch"); Distribution distribution = Distribution.init(this); // Init suggested sites engine in BrowserDB. final SuggestedSites suggestedSites = new SuggestedSites(appContext, distribution); @@ -1319,16 +1320,17 @@ public class BrowserApp extends GeckoApp "CharEncoding:State", "Favicon:CacheLoad", "Feedback:LastUrl", "Feedback:MaybeLater", "Feedback:OpenPlayStore", "Menu:Add", "Menu:Remove", "Reader:Share", + "Sanitize:ClearHistory", "Settings:Show", "Telemetry:Gather", "Updater:Launch"); if (AppConstants.MOZ_ANDROID_BEAM) { NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this); if (nfc != null) { // null this out even though the docs say it's not needed, @@ -1355,16 +1357,26 @@ public class BrowserApp extends GeckoApp // Initialize the actionbar menu items on startup for both large and small tablets if (HardwareUtils.isTablet()) { onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, null); invalidateOptionsMenu(); } } + private void handleClearHistory(final boolean clearSearchHistory) { + final BrowserDB db = getProfile().getDB(); + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + db.clearHistory(getContentResolver(), clearSearchHistory); + } + }); + } + private void shareCurrentUrl() { Tab tab = Tabs.getInstance().getSelectedTab(); if (tab == null) { return; } String url = tab.getURL(); if (url == null) { @@ -1631,17 +1643,19 @@ public class BrowserApp extends GeckoApp removeAddonMenuItem(id); } }); } else if ("Reader:Share".equals(event)) { final String title = message.getString("title"); final String url = message.getString("url"); GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title); - + } else if ("Sanitize:ClearHistory".equals(event)) { + handleClearHistory(message.optBoolean("clearSearchHistory", false)); + callback.sendSuccess(true); } else if ("Settings:Show".equals(event)) { final String resource = message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null); final Intent settingsIntent = new Intent(this, GeckoPreferences.class); GeckoPreferences.setResourceToOpen(settingsIntent, resource); startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES); // Don't use a transition to settings if we're on a device where that
--- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -518,26 +518,16 @@ public abstract class GeckoApp @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground()); outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession); } - void handleClearHistory(final boolean clearSearchHistory) { - final BrowserDB db = getProfile().getDB(); - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - db.clearHistory(getContentResolver(), clearSearchHistory); - } - }); - } - public void addTab() { } public void addPrivateTab() { } public void showNormalTabs() { } public void showPrivateTabs() { } @@ -620,20 +610,16 @@ public abstract class GeckoApp } else if ("Permissions:Data".equals(event)) { String host = message.getString("host"); final NativeJSObject[] permissions = message.getObjectArray("permissions"); showSiteSettingsDialog(host, permissions); } else if ("PrivateBrowsing:Data".equals(event)) { mPrivateBrowsingSession = message.optString("session", null); - } else if ("Sanitize:ClearHistory".equals(event)) { - handleClearHistory(message.optBoolean("clearSearchHistory", false)); - callback.sendSuccess(true); - } else if ("Session:StatePurged".equals(event)) { onStatePurged(); } else if ("Share:Text".equals(event)) { String text = message.getString("text"); GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, ""); // Context: Sharing via chrome list (no explicit session is active) @@ -1542,17 +1528,16 @@ public abstract class GeckoApp "Bookmark:Insert", "Contact:Add", "DOMFullScreen:Start", "DOMFullScreen:Stop", "Image:SetAs", "Locale:Set", "Permissions:Data", "PrivateBrowsing:Data", - "Sanitize:ClearHistory", "Session:StatePurged", "Share:Text", "SystemUI:Visibility", "Toast:Show", "ToggleChrome:Focus", "ToggleChrome:Hide", "ToggleChrome:Show", "Update:Check", @@ -2034,17 +2019,16 @@ public abstract class GeckoApp "Bookmark:Insert", "Contact:Add", "DOMFullScreen:Start", "DOMFullScreen:Stop", "Image:SetAs", "Locale:Set", "Permissions:Data", "PrivateBrowsing:Data", - "Sanitize:ClearHistory", "Session:StatePurged", "Share:Text", "SystemUI:Visibility", "Toast:Show", "ToggleChrome:Focus", "ToggleChrome:Hide", "ToggleChrome:Show", "Update:Check",
--- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -289,16 +289,19 @@ public class GeckoAppShell // a composition, so there is no need to schedule a composition after // resuming. public static native void scheduleResumeComposition(int width, int height); public static native float computeRenderIntegrity(); public static native SurfaceBits getSurfaceBits(Surface surface); + public static native void addPresentationSurface(Surface surface); + public static native void removePresentationSurface(Surface surface); + public static native void onFullScreenPluginHidden(View view); public static class CreateShortcutFaviconLoadedListener implements OnFaviconLoadedListener { private final String title; private final String url; public CreateShortcutFaviconLoadedListener(final String url, final String title) { this.url = url;
--- a/mobile/android/base/MediaPlayerManager.java +++ b/mobile/android/base/MediaPlayerManager.java @@ -1,22 +1,32 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- * 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/. */ package org.mozilla.gecko; +import android.app.Presentation; +import android.content.Context; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouter; import android.support.v7.media.MediaRouter.RouteInfo; import android.util.Log; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowManager; import com.google.android.gms.cast.CastMediaControlIntent; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.mozglue.JNITarget; import org.mozilla.gecko.util.EventCallback; @@ -56,16 +66,17 @@ public class MediaPlayerManager extends private static void debug(String msg) { if (SHOW_DEBUG) { Log.d(LOGTAG, msg); } } private MediaRouter mediaRouter = null; private final Map<String, GeckoMediaPlayer> displays = new HashMap<String, GeckoMediaPlayer>(); + private GeckoPresentation presentation = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventDispatcher.getInstance().registerGeckoThreadListener(this, "MediaPlayer:Load", "MediaPlayer:Start", "MediaPlayer:Stop", @@ -130,47 +141,53 @@ public class MediaPlayerManager extends private final MediaRouter.Callback callback = new MediaRouter.Callback() { @Override public void onRouteRemoved(MediaRouter router, RouteInfo route) { debug("onRouteRemoved: route=" + route); displays.remove(route.getId()); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent( "MediaPlayer:Removed", route.getId())); + updatePresentation(); } @SuppressWarnings("unused") public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo route) { + updatePresentation(); } // These methods aren't used by the support version Media Router @SuppressWarnings("unused") public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) { + updatePresentation(); } @Override public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { + updatePresentation(); } @Override public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { } @Override public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) { debug("onRouteAdded: route=" + route); final GeckoMediaPlayer display = getMediaPlayerForRoute(route); saveAndNotifyOfDisplay("MediaPlayer:Added", route, display); + updatePresentation(); } @Override public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) { debug("onRouteChanged: route=" + route); final GeckoMediaPlayer display = displays.get(route.getId()); saveAndNotifyOfDisplay("MediaPlayer:Changed", route, display); + updatePresentation(); } private void saveAndNotifyOfDisplay(final String eventName, MediaRouter.RouteInfo route, final GeckoMediaPlayer display) { if (display == null) { return; } @@ -216,9 +233,91 @@ public class MediaPlayerManager extends mediaRouter = MediaRouter.getInstance(getActivity()); final MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder() .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCast.MIRROR_RECEIVER_APP_ID)) .build(); mediaRouter.addCallback(selectorBuilder, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); } + + @Override + public void onStop() { + super.onStop(); + if (presentation != null) { + presentation.dismiss(); + presentation = null; + } + } + + private void updatePresentation() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return; + } + + if (mediaRouter == null) { + return; + } + + MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); + Display display = route != null ? route.getPresentationDisplay() : null; + + if (display != null) { + if ((presentation != null) && (presentation.getDisplay() != display)) { + presentation.dismiss(); + presentation = null; + } + + if (presentation == null) { + presentation = new GeckoPresentation(getActivity(), display); + + try { + presentation.show(); + } catch (WindowManager.InvalidDisplayException ex) { + Log.w(LOGTAG, "Couldn't show presentation! Display was removed in " + + "the meantime.", ex); + presentation = null; + } + } + } else if (presentation != null) { + presentation.dismiss(); + presentation = null; + } + } + + private static class SurfaceListener implements SurfaceHolder.Callback { + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + // Surface changed so force a composite + GeckoAppShell.scheduleComposite(); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + GeckoAppShell.addPresentationSurface(holder.getSurface()); + GeckoAppShell.scheduleComposite(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + GeckoAppShell.removePresentationSurface(holder.getSurface()); + } + } + + private final static class GeckoPresentation extends Presentation { + private SurfaceView mView; + public GeckoPresentation(Context context, Display display) { + super(context, display); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mView = new SurfaceView(getContext()); + setContentView(mView, new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + mView.getHolder().addCallback(new SurfaceListener()); + } + } }
--- a/mobile/android/base/TextSelection.java +++ b/mobile/android/base/TextSelection.java @@ -1,26 +1,28 @@ /* 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/. */ package org.mozilla.gecko; +import android.content.res.Resources; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.gfx.BitmapUtils.BitmapLoader; import org.mozilla.gecko.gfx.Layer; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.LayerView.DrawListener; import org.mozilla.gecko.menu.GeckoMenu; import org.mozilla.gecko.menu.GeckoMenuItem; import org.mozilla.gecko.EventDispatcher; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.ActionModeCompat.Callback; +import org.mozilla.gecko.AppConstants.Versions; import android.content.Context; import android.app.Activity; import android.graphics.drawable.Drawable; import android.view.Menu; import android.view.MenuItem; import org.json.JSONArray; @@ -274,21 +276,33 @@ class TextSelection extends Layer implem int length = mItems.length(); for (int i = 0; i < length; i++) { try { final JSONObject obj = mItems.getJSONObject(i); final GeckoMenuItem menuitem = (GeckoMenuItem) menu.add(0, i, 0, obj.optString("label")); final int actionEnum = obj.optBoolean("showAsAction") ? GeckoMenuItem.SHOW_AS_ACTION_ALWAYS : GeckoMenuItem.SHOW_AS_ACTION_NEVER; menuitem.setShowAsAction(actionEnum, R.attr.menuItemActionModeStyle); - BitmapUtils.getDrawable(anchorHandle.getContext(), obj.optString("icon"), new BitmapLoader() { + final String iconString = obj.optString("icon"); + BitmapUtils.getDrawable(anchorHandle.getContext(), iconString, new BitmapLoader() { @Override public void onBitmapFound(Drawable d) { if (d != null) { menuitem.setIcon(d); + + // Dynamically add padding to align the share icon on GB devices. + // To be removed in bug 1122752. + if (Versions.preHC && "drawable://ic_menu_share".equals(iconString)) { + final View view = menuitem.getActionView(); + + final Resources res = view.getContext().getResources(); + final int padding = res.getDimensionPixelSize(R.dimen.ab_share_padding); + + view.setPadding(padding, padding, padding, padding); + } } } }); } catch(Exception ex) { Log.i(LOGTAG, "Exception building menu", ex); } } return true;
--- a/mobile/android/base/resources/values/dimens.xml +++ b/mobile/android/base/resources/values/dimens.xml @@ -208,16 +208,20 @@ <dimen name="find_in_page_text_margin_left">5dip</dimen> <dimen name="find_in_page_text_margin_right">12dip</dimen> <dimen name="find_in_page_text_padding_left">10dip</dimen> <dimen name="find_in_page_text_padding_right">10dip</dimen> <dimen name="find_in_page_status_margin_right">10dip</dimen> <dimen name="find_in_page_matchcase_padding">10dip</dimen> <dimen name="find_in_page_control_margin_top">2dip</dimen> + <!-- The share icon asset has no padding while the other action bar items do + so we dynamically add padding to compensate. To be removed in bug 1122752. --> + <dimen name="ab_share_padding">12dp</dimen> + <!-- This is a 4:7 ratio (as per UX decision). --> <item name="thumbnail_aspect_ratio" format="float" type="dimen">0.571</item> <!-- http://blog.danlew.net/2015/01/06/handling-android-resources-with-non-standard-formats/ --> <item name="match_parent" type="dimen">-1</item> <item name="wrap_content" type="dimen">-2</item> </resources>
--- a/mobile/android/chrome/content/Reader.js +++ b/mobile/android/chrome/content/Reader.js @@ -1,17 +1,15 @@ // -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /* 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/. */ "use strict"; -XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm"); - let Reader = { // These values should match those defined in BrowserContract.java. STATUS_UNFETCHED: 0, STATUS_FETCH_FAILED_TEMPORARY: 1, STATUS_FETCH_FAILED_PERMANENT: 2, STATUS_FETCH_FAILED_UNSUPPORTED_FORMAT: 3, STATUS_FETCHED_ARTICLE: 4,
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -108,16 +108,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource://gre/modules/SharedPreferences.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm"); + let lazilyLoadedBrowserScripts = [ ["SelectHelper", "chrome://browser/content/SelectHelper.js"], ["InputWidgetHelper", "chrome://browser/content/InputWidgetHelper.js"], ["MasterPassword", "chrome://browser/content/MasterPassword.js"], ["PluginHelper", "chrome://browser/content/PluginHelper.js"], ["OfflineApps", "chrome://browser/content/OfflineApps.js"], ["Linkifier", "chrome://browser/content/Linkify.js"], ["ZoomHelper", "chrome://browser/content/ZoomHelper.js"], @@ -4557,26 +4559,17 @@ Tab.prototype = { this.contentDocumentIsDisplayed = false; this.hasTouchListener = false; } else { this.sendViewportUpdate(); } }, _stripAboutReaderURL: function (url) { - if (!url.startsWith("about:reader")) { - return url; - } - - // From ReaderParent._getOriginalUrl (browser/modules/ReaderParent.jsm). - let searchParams = new URLSearchParams(url.substring("about:reader?".length)); - if (!searchParams.has("url")) { - return url; - } - return decodeURIComponent(searchParams.get("url")); + return ReaderMode.getOriginalUrl(url) || url; }, // Properties used to cache security state used to update the UI _state: null, _hostChanged: false, // onLocationChange will flip this bit onSecurityChange: function(aWebProgress, aRequest, aState) { // Don't need to do anything if the data we use to update the UI hasn't changed
--- a/mozglue/android/jni-stubs.inc +++ b/mozglue/android/jni-stubs.inc @@ -300,16 +300,54 @@ Java_org_mozilla_gecko_GeckoAppShell_get #endif #ifdef JNI_BINDINGS xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_getSurfaceBits", &f_Java_org_mozilla_gecko_GeckoAppShell_getSurfaceBits); #endif #ifdef JNI_STUBS +typedef void (*Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface_t)(JNIEnv *, jclass, jobject); +static Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface_t f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface; +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface(JNIEnv * arg0, jclass arg1, jobject arg2) { + if (!f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface) { + arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), + "JNI Function called before it was loaded"); + return ; + } + f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface(arg0, arg1, arg2); +} +#endif + +#ifdef JNI_BINDINGS + xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface", &f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface); +#endif + +#ifdef JNI_STUBS + +typedef void (*Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface_t)(JNIEnv *, jclass, jobject); +static Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface_t f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface; +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface(JNIEnv * arg0, jclass arg1, jobject arg2) { + if (!f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface) { + arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), + "JNI Function called before it was loaded"); + return ; + } + f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface(arg0, arg1, arg2); +} +#endif + +#ifdef JNI_BINDINGS + xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface", &f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface); +#endif + +#ifdef JNI_STUBS + typedef void (*Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden_t)(JNIEnv *, jclass, jobject); static Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden_t f_Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden; extern "C" NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden(JNIEnv * arg0, jclass arg1, jobject arg2) { if (!f_Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden) { arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), "JNI Function called before it was loaded"); return ;
--- a/toolkit/components/build/nsToolkitCompsCID.h +++ b/toolkit/components/build/nsToolkitCompsCID.h @@ -1,18 +1,12 @@ /* 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/. */ -#if defined(MOZ_UPDATER) -# if !defined(MOZ_WIDGET_ANDROID) -# define USE_MOZ_UPDATER -# endif -#endif - #define NS_ALERTSERVICE_CONTRACTID \ "@mozilla.org/alerts-service;1" // This separate service uses the same nsIAlertsService interface, // but instead sends a notification to a platform alerts API // if available. Using a separate CID allows us to overwrite the XUL // alerts service at runtime. #define NS_SYSTEMALERTSERVICE_CONTRACTID \ @@ -85,17 +79,17 @@ "@mozilla.org/browser/tagging-service;1" #define NS_FAVICONSERVICE_CONTRACTID \ "@mozilla.org/browser/favicon-service;1" #define NS_APPSTARTUP_CONTRACTID \ "@mozilla.org/toolkit/app-startup;1" -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) #define NS_UPDATEPROCESSOR_CONTRACTID \ "@mozilla.org/updates/update-processor;1" #endif #define NS_ADDONPATHSERVICE_CONTRACTID \ "@mozilla.org/addon-path-service;1" ///////////////////////////////////////////////////////////////////////////// @@ -168,17 +162,17 @@ { 0x5e8d4751, 0x1852, 0x434b, { 0xa9, 0x92, 0x2c, 0x6d, 0x2a, 0x25, 0xfa, 0x46 } } #define NS_NAVBOOKMARKSSERVICE_CID \ { 0x9de95a0c, 0x39a4, 0x4d64, {0x9a, 0x53, 0x17, 0x94, 0x0d, 0xd7, 0xca, 0xbb}} #define NS_FAVICONSERVICE_CID \ { 0x984e3259, 0x9266, 0x49cf, { 0xb6, 0x05, 0x60, 0xb0, 0x22, 0xa0, 0x07, 0x56 } } -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) #define NS_UPDATEPROCESSOR_CID \ { 0xf3dcf644, 0x79e8, 0x4f59, { 0xa1, 0xbb, 0x87, 0x84, 0x54, 0x48, 0x8e, 0xf9 } } #endif #define NS_APPLICATION_REPUTATION_SERVICE_CONTRACTID \ "@mozilla.org/downloads/application-reputation-service;1" #define NS_APPLICATION_REPUTATION_SERVICE_CID \
--- a/toolkit/components/build/nsToolkitCompsModule.cpp +++ b/toolkit/components/build/nsToolkitCompsModule.cpp @@ -2,17 +2,17 @@ * 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/. */ #include "mozilla/ModuleUtils.h" #include "nsAppStartup.h" #include "nsUserInfo.h" #include "nsToolkitCompsCID.h" #include "nsFindService.h" -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) #include "nsUpdateDriver.h" #endif #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS) #include "nsParentalControlsService.h" #endif #include "nsAlertsService.h" @@ -104,17 +104,17 @@ nsUrlClassifierDBServiceConstructor(nsIS rv = inst->QueryInterface(aIID, aResult); NS_RELEASE(inst); return rv; } #endif NS_GENERIC_FACTORY_CONSTRUCTOR(nsBrowserStatusFilter) -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateProcessor) #endif NS_GENERIC_FACTORY_CONSTRUCTOR(FinalizationWitnessService) NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance) @@ -136,17 +136,17 @@ NS_DEFINE_NAMED_CID(NS_TYPEAHEADFIND_CID #ifdef MOZ_URL_CLASSIFIER NS_DEFINE_NAMED_CID(NS_APPLICATION_REPUTATION_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERPREFIXSET_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERDBSERVICE_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERSTREAMUPDATER_CID); NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERUTILS_CID); #endif NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILTER_CID); -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID); #endif NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID); NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID); NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID); static const Module::CIDEntry kToolkitCIDs[] = { @@ -168,17 +168,17 @@ static const Module::CIDEntry kToolkitCI #ifdef MOZ_URL_CLASSIFIER { &kNS_APPLICATION_REPUTATION_SERVICE_CID, false, nullptr, ApplicationReputationServiceConstructor }, { &kNS_URLCLASSIFIERPREFIXSET_CID, false, nullptr, nsUrlClassifierPrefixSetConstructor }, { &kNS_URLCLASSIFIERDBSERVICE_CID, false, nullptr, nsUrlClassifierDBServiceConstructor }, { &kNS_URLCLASSIFIERSTREAMUPDATER_CID, false, nullptr, nsUrlClassifierStreamUpdaterConstructor }, { &kNS_URLCLASSIFIERUTILS_CID, false, nullptr, nsUrlClassifierUtilsConstructor }, #endif { &kNS_BROWSERSTATUSFILTER_CID, false, nullptr, nsBrowserStatusFilterConstructor }, -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) { &kNS_UPDATEPROCESSOR_CID, false, nullptr, nsUpdateProcessorConstructor }, #endif { &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor }, { &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor }, { &kNS_ADDON_PATH_SERVICE_CID, false, nullptr, AddonPathServiceConstructor }, { &kNATIVE_FILEWATCHER_SERVICE_CID, false, nullptr, NativeFileWatcherServiceConstructor }, { nullptr } }; @@ -202,17 +202,17 @@ static const Module::ContractIDEntry kTo { NS_APPLICATION_REPUTATION_SERVICE_CONTRACTID, &kNS_APPLICATION_REPUTATION_SERVICE_CID }, { NS_URLCLASSIFIERPREFIXSET_CONTRACTID, &kNS_URLCLASSIFIERPREFIXSET_CID }, { NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID }, { NS_URICLASSIFIERSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID }, { NS_URLCLASSIFIERSTREAMUPDATER_CONTRACTID, &kNS_URLCLASSIFIERSTREAMUPDATER_CID }, { NS_URLCLASSIFIERUTILS_CONTRACTID, &kNS_URLCLASSIFIERUTILS_CID }, #endif { NS_BROWSERSTATUSFILTER_CONTRACTID, &kNS_BROWSERSTATUSFILTER_CID }, -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) { NS_UPDATEPROCESSOR_CONTRACTID, &kNS_UPDATEPROCESSOR_CID }, #endif { FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID }, { NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID }, { NS_ADDONPATHSERVICE_CONTRACTID, &kNS_ADDON_PATH_SERVICE_CID }, { NATIVE_FILEWATCHER_SERVICE_CONTRACTID, &kNATIVE_FILEWATCHER_SERVICE_CID }, { nullptr } };
--- a/toolkit/components/moz.build +++ b/toolkit/components/moz.build @@ -43,16 +43,17 @@ DIRS += [ 'protobuf', 'reader', 'reflect', 'sqlite', 'startup', 'statusfilter', 'telemetry', 'thumbnails', + 'timermanager', 'typeaheadfind', 'urlformatter', 'viewconfig', 'viewsource', 'workerloader', 'workerlz4', 'xulstore' ]
--- a/toolkit/components/reader/AboutReader.jsm +++ b/toolkit/components/reader/AboutReader.jsm @@ -3,16 +3,17 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils; this.EXPORTED_SYMBOLS = [ "AboutReader" ]; +Cu.import("resource://gre/modules/ReaderMode.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm"); const READINGLIST_COMMAND_ID = "readingListSidebar"; @@ -729,19 +730,18 @@ AboutReader.prototype = { _showContent: function Reader_showContent(article) { this._messageElement.style.display = "none"; this._article = article; this._domainElement.href = article.url; let articleUri = Services.io.newURI(article.url, null, null); - this._domainElement.innerHTML = this._stripHost(articleUri.host); - - this._creditsElement.innerHTML = article.byline; + this._domainElement.textContent = this._stripHost(articleUri.host); + this._creditsElement.textContent = article.byline; this._titleElement.textContent = article.title; this._doc.title = article.title; this._headerElement.style.display = "block"; let parserUtils = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils); let contentFragment = parserUtils.parseFragment(article.content, @@ -782,22 +782,17 @@ AboutReader.prototype = { }.bind(this), 300); }, /** * Returns the original article URL for this about:reader view. */ _getOriginalUrl: function(win) { let url = win ? win.location.href : this._win.location.href; - let searchParams = new URLSearchParams(url.split("?")[1]); - if (!searchParams.has("url")) { - Cu.reportError("Error finding original URL for about:reader URL: " + url); - return url; - } - return decodeURIComponent(searchParams.get("url")); + return ReaderMode.getOriginalUrl(url) || url; }, _setupSegmentedButton: function Reader_setupSegmentedButton(id, options, initialValue, callback) { let doc = this._doc; let segmentedButton = doc.getElementById(id); for (let i = 0; i < options.length; i++) { let option = options[i];
--- a/toolkit/components/reader/ReaderMode.jsm +++ b/toolkit/components/reader/ReaderMode.jsm @@ -63,16 +63,41 @@ this.ReaderMode = { if (aData.startsWith("reader.parse-on-load.")) { this.isEnabledForParseOnLoad = this._getStateForParseOnLoad(); } break; } }, /** + * Returns original URL from an about:reader URL. + * + * @param url An about:reader URL. + * @return The original URL for the article, or null if we did not find + * a properly formatted about:reader URL. + */ + getOriginalUrl: function(url) { + if (!url.startsWith("about:reader?")) { + return null; + } + + let searchParams = new URLSearchParams(url.substring("about:reader?".length)); + if (!searchParams.has("url")) { + return null; + } + let encodedURL = searchParams.get("url"); + try { + return decodeURIComponent(encodedURL); + } catch (e) { + Cu.reportError("Error decoding original URL: " + e); + return encodedURL; + } + }, + + /** * Decides whether or not a document is reader-able without parsing the whole thing. * * @param doc A document to parse. * @return boolean Whether or not we should show the reader mode button. */ isProbablyReaderable: function(doc) { // Only care about 'real' HTML documents: if (doc.mozSyntheticDocument || !(doc instanceof doc.defaultView.HTMLDocument)) {
--- a/toolkit/components/satchel/AutoCompleteE10S.jsm +++ b/toolkit/components/satchel/AutoCompleteE10S.jsm @@ -79,21 +79,22 @@ this.AutoCompleteE10S = { init: function() { let messageManager = Cc["@mozilla.org/globalmessagemanager;1"]. getService(Ci.nsIMessageListenerManager); messageManager.addMessageListener("FormAutoComplete:SelectBy", this); messageManager.addMessageListener("FormAutoComplete:GetSelectedIndex", this); messageManager.addMessageListener("FormAutoComplete:ClosePopup", this); }, - _initPopup: function(browserWindow, rect) { + _initPopup: function(browserWindow, rect, direction) { this.browser = browserWindow.gBrowser.selectedBrowser; this.popup = this.browser.autoCompletePopup; this.popup.hidden = false; this.popup.setAttribute("width", rect.width); + this.popup.style.direction = direction; this.x = rect.left; this.y = rect.top + rect.height; }, _showPopup: function(results) { AutoCompleteE10SView.clearResults(); @@ -132,18 +133,19 @@ this.AutoCompleteE10S = { }, // This function is called in response to AutoComplete requests from the // child (received via the message manager, see // "FormHistory:AutoCompleteSearchAsync"). search: function(message) { let browserWindow = message.target.ownerDocument.defaultView; let rect = message.data; + let direction = message.data.direction; - this._initPopup(browserWindow, rect); + this._initPopup(browserWindow, rect, direction); let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"] .getService(Ci.nsIFormAutoComplete); formAutoComplete.autoCompleteSearchAsync(message.data.inputName, message.data.untrimmedSearchString, null, null,
--- a/toolkit/components/satchel/nsFormAutoComplete.js +++ b/toolkit/components/satchel/nsFormAutoComplete.js @@ -367,34 +367,36 @@ FormAutoCompleteChild.prototype = { autoCompleteSearchAsync : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) { this.log("autoCompleteSearchAsync"); if (this._pendingSearch) { this.stopAutoCompleteSearch(); } - let rect = BrowserUtils.getElementBoundingScreenRect(aField); + let window = aField.ownerDocument.defaultView; - let window = aField.ownerDocument.defaultView; + let rect = BrowserUtils.getElementBoundingScreenRect(aField); + let direction = window.getComputedStyle(aField).direction; let topLevelDocshell = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDocShell) .sameTypeRootTreeItem .QueryInterface(Ci.nsIDocShell); let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIContentFrameMessageManager); mm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", { inputName: aInputName, untrimmedSearchString: aUntrimmedSearchString, left: rect.left, top: rect.top, width: rect.width, - height: rect.height + height: rect.height, + direction: direction, }); let search = this._pendingSearch = {}; let searchFinished = message => { mm.removeMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult", searchFinished); // Check whether stopAutoCompleteSearch() was called, i.e. the search // was cancelled, while waiting for a result.
copy from toolkit/mozapps/update/moz.build copy to toolkit/components/timermanager/moz.build --- a/toolkit/mozapps/update/moz.build +++ b/toolkit/components/timermanager/moz.build @@ -1,50 +1,21 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. -if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': - if CONFIG['MOZ_UPDATER'] or CONFIG['MOZ_MAINTENANCE_SERVICE']: - # If only the maintenance service is installed and not - # the updater, then the maintenance service may still be - # used for other things. We need to build update/common - # which the maintenance service uses. - DIRS += ['common'] - if CONFIG['OS_ARCH'] == 'WINNT': - DIRS += ['common-standalone'] - - if CONFIG['MOZ_UPDATER']: - DIRS += ['updater'] - XPIDL_MODULE = 'update' -XPCSHELL_TESTS_MANIFESTS += ['tests/unit_timermanager/xpcshell.ini'] +XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini'] XPIDL_SOURCES += [ 'nsIUpdateTimerManager.idl', ] EXTRA_COMPONENTS += [ 'nsUpdateTimerManager.js', 'nsUpdateTimerManager.manifest', ] -if CONFIG['MOZ_UPDATER']: - TEST_DIRS += ['tests'] - - XPIDL_SOURCES += [ - 'nsIUpdateService.idl', - ] - - EXTRA_COMPONENTS += [ - 'nsUpdateService.js', - 'nsUpdateService.manifest', - 'nsUpdateServiceStub.js', - ] - - EXTRA_JS_MODULES += [ - 'UpdateTelemetry.jsm', - ] - -JAR_MANIFESTS += ['jar.mn'] +with Files('**'): + BUG_COMPONENT = ('Toolkit', 'Application Update')
rename from toolkit/mozapps/update/nsIUpdateTimerManager.idl rename to toolkit/components/timermanager/nsIUpdateTimerManager.idl
rename from toolkit/mozapps/update/nsUpdateTimerManager.js rename to toolkit/components/timermanager/nsUpdateTimerManager.js
rename from toolkit/mozapps/update/nsUpdateTimerManager.manifest rename to toolkit/components/timermanager/nsUpdateTimerManager.manifest
rename from toolkit/mozapps/update/tests/unit_timermanager/consumerNotifications.js rename to toolkit/components/timermanager/tests/unit/consumerNotifications.js
rename from toolkit/mozapps/update/tests/unit_timermanager/xpcshell.ini rename to toolkit/components/timermanager/tests/unit/xpcshell.ini
--- a/toolkit/modules/NewTabUtils.jsm +++ b/toolkit/modules/NewTabUtils.jsm @@ -467,50 +467,64 @@ let PinnedLinks = { } }; /** * Singleton that keeps track of all blocked links in the grid. */ let BlockedLinks = { /** + * A list of objects that are observing blocked link changes. + */ + _observers: [], + + /** * The cached list of blocked links. */ _links: null, /** + * Registers an object that will be notified when the blocked links change. + */ + addObserver: function (aObserver) { + this._observers.push(aObserver); + }, + + /** * The list of blocked links. */ get links() { if (!this._links) this._links = Storage.get("blockedLinks", {}); return this._links; }, /** - * Blocks a given link. + * Blocks a given link. Adjusts siteMap accordingly, and notifies listeners. * @param aLink The link to block. */ block: function BlockedLinks_block(aLink) { + this._callObservers("onLinkBlocked", aLink); this.links[toHash(aLink.url)] = 1; this.save(); // Make sure we unpin blocked links. PinnedLinks.unpin(aLink); }, /** - * Unblocks a given link. + * Unblocks a given link. Adjusts siteMap accordingly, and notifies listeners. * @param aLink The link to unblock. */ unblock: function BlockedLinks_unblock(aLink) { if (this.isBlocked(aLink)) { delete this.links[toHash(aLink.url)]; this.save(); + this._callObservers("onLinkUnblocked", aLink); } }, /** * Saves the current list of blocked links. */ save: function BlockedLinks_save() { Storage.set("blockedLinks", this.links); @@ -532,16 +546,28 @@ let BlockedLinks = { return Object.keys(this.links).length == 0; }, /** * Resets the links cache. */ resetCache: function BlockedLinks_resetCache() { this._links = null; + }, + + _callObservers(methodName, ...args) { + for (let obs of this._observers) { + if (typeof(obs[methodName]) == "function") { + try { + obs[methodName](...args); + } catch (err) { + Cu.reportError(err); + } + } + } } }; /** * Singleton that serves as the default link provider for the grid. It queries * the history to retrieve the most frequently visited sites. */ let PlacesProvider = { @@ -714,16 +740,18 @@ let Links = { * The maximum number of links returned by getLinks. */ maxNumLinks: LINKS_GET_LINKS_LIMIT, /** * A mapping from each provider to an object { sortedLinks, siteMap, linkMap }. * sortedLinks is the cached, sorted array of links for the provider. * siteMap is a mapping from base domains to URL count associated with the domain. + * The count does not include blocked URLs. siteMap is used to look up a + * user's top sites that can be targeted with a suggested tile. * linkMap is a Map from link URLs to link objects. */ _providers: new Map(), /** * The properties of link objects used to sort them. */ _sortProperties: [ @@ -733,16 +761,28 @@ let Links = { ], /** * List of callbacks waiting for the cache to be populated. */ _populateCallbacks: [], /** + * A list of objects that are observing links updates. + */ + _observers: [], + + /** + * Registers an object that will be notified when links updates. + */ + addObserver: function (aObserver) { + this._observers.push(aObserver); + }, + + /** * Adds a link provider. * @param aProvider The link provider. */ addProvider: function Links_addProvider(aProvider) { this._providers.set(aProvider, null); aProvider.addObserver(this); }, @@ -856,30 +896,69 @@ let Links = { throw new Error("Comparable link missing required property: " + prop); } return aLink2.frecency - aLink1.frecency || aLink2.lastVisitDate - aLink1.lastVisitDate || aLink1.url.localeCompare(aLink2.url); }, _incrementSiteMap: function(map, link) { + if (NewTabUtils.blockedLinks.isBlocked(link)) { + // Don't count blocked URLs. + return; + } let site = NewTabUtils.extractSite(link.url); map.set(site, (map.get(site) || 0) + 1); }, _decrementSiteMap: function(map, link) { + if (NewTabUtils.blockedLinks.isBlocked(link)) { + // Blocked URLs are not included in map. + return; + } let site = NewTabUtils.extractSite(link.url); let previousURLCount = map.get(site); if (previousURLCount === 1) { map.delete(site); } else { map.set(site, previousURLCount - 1); } }, + /** + * Update the siteMap cache based on the link given and whether we need + * to increment or decrement it. We do this by iterating over all stored providers + * to find which provider this link already exists in. For providers that + * have this link, we will adjust siteMap for them accordingly. + * + * @param aLink The link that will affect siteMap + * @param increment A boolean for whether to increment or decrement siteMap + */ + _adjustSiteMapAndNotify: function(aLink, increment=true) { + for (let [provider, cache] of this._providers) { + // We only update siteMap if aLink is already stored in linkMap. + if (cache.linkMap.get(aLink.url)) { + if (increment) { + this._incrementSiteMap(cache.siteMap, aLink); + continue; + } + this._decrementSiteMap(cache.siteMap, aLink); + } + } + this._callObservers("onLinkChanged", aLink); + }, + + onLinkBlocked: function(aLink) { + this._adjustSiteMapAndNotify(aLink, false); + }, + + onLinkUnblocked: function(aLink) { + this._adjustSiteMapAndNotify(aLink); + }, + populateProviderCache: function(provider, callback) { if (!this._providers.has(provider)) { throw new Error("Can only populate provider cache for existing provider."); } return this._populateProviderCache(provider, callback, false); }, @@ -1090,16 +1169,28 @@ let Links = { // Make sure to update open about:newtab instances. If there are no opened // pages we can just wait for the next new tab to populate the cache again. if (AllPages.length && AllPages.enabled) this.populateCache(function () { AllPages.update() }, true); else this.resetCache(); }, + _callObservers(methodName, ...args) { + for (let obs of this._observers) { + if (typeof(obs[methodName]) == "function") { + try { + obs[methodName](this, ...args); + } catch (err) { + Cu.reportError(err); + } + } + } + }, + /** * Adds a sanitization observer and turns itself into a no-op after the first * invokation. */ _addObserver: function Links_addObserver() { Services.obs.addObserver(this, "browser:purge-session-history", true); this._addObserver = function () {}; }, @@ -1230,16 +1321,17 @@ this.NewTabUtils = { // Strip off common subdomains of the same site (e.g., www, load balancer) return uri.asciiHost.replace(/^(m|mobile|www\d*)\./, ""); }, init: function NewTabUtils_init() { if (this.initWithoutProviders()) { PlacesProvider.init(); Links.addProvider(PlacesProvider); + BlockedLinks.addObserver(Links); } }, initWithoutProviders: function NewTabUtils_initWithoutProviders() { if (!this._initialized) { this._initialized = true; ExpirationFilter.init(); Telemetry.init();
--- a/toolkit/moz.build +++ b/toolkit/moz.build @@ -18,20 +18,27 @@ DIRS += [ 'mozapps/preferences', 'mozapps/plugins', 'obsolete', 'profile', 'themes', 'webapps', ] -DIRS += ['mozapps/update'] +if CONFIG['MOZ_UPDATER'] and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': + DIRS += ['mozapps/update'] if CONFIG['MOZ_MAINTENANCE_SERVICE']: - DIRS += ['components/maintenanceservice'] +# Including mozapps/update/common-standalone allows the maintenance service +# to be built so the maintenance service can be used for things other than +# updating applications. + DIRS += [ + 'mozapps/update/common-standalone', + 'components/maintenanceservice' + ] DIRS += ['xre'] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'qt'): DIRS += ['system/unixproxy'] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': DIRS += ['system/osxproxy'] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': @@ -56,11 +63,8 @@ elif CONFIG['MOZ_ENABLE_PROFILER_SPS']: with Files('mozapps/installer/windows/*'): BUG_COMPONENT = ('Toolkit', 'NSIS Installer') with Files('mozapps/plugins/*'): BUG_COMPONENT = ('Core', 'Plug-ins') with Files('mozapps/preferences/*'): BUG_COMPONENT = ('Toolkit', 'Preferences') - -with Files('mozapps/update/*'): - BUG_COMPONENT = ('Toolkit', 'Application Update')
--- a/toolkit/mozapps/update/common-standalone/moz.build +++ b/toolkit/mozapps/update/common-standalone/moz.build @@ -5,8 +5,10 @@ Library('updatecommon-standalone') srcdir = '../common' include('../common/sources.mozbuild') if CONFIG['OS_ARCH'] == 'WINNT': USE_STATIC_LIBS = True + +FAIL_ON_WARNINGS = True
--- a/toolkit/mozapps/update/common/moz.build +++ b/toolkit/mozapps/update/common/moz.build @@ -20,8 +20,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind Library('updatecommon') srcdir = '.' include('sources.mozbuild') FINAL_LIBRARY = 'xul' + +FAIL_ON_WARNINGS = True
--- a/toolkit/mozapps/update/moz.build +++ b/toolkit/mozapps/update/moz.build @@ -1,50 +1,35 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. -if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': - if CONFIG['MOZ_UPDATER'] or CONFIG['MOZ_MAINTENANCE_SERVICE']: - # If only the maintenance service is installed and not - # the updater, then the maintenance service may still be - # used for other things. We need to build update/common - # which the maintenance service uses. - DIRS += ['common'] - if CONFIG['OS_ARCH'] == 'WINNT': - DIRS += ['common-standalone'] - - if CONFIG['MOZ_UPDATER']: - DIRS += ['updater'] - XPIDL_MODULE = 'update' -XPCSHELL_TESTS_MANIFESTS += ['tests/unit_timermanager/xpcshell.ini'] +DIRS += [ + 'common', + 'updater', +] XPIDL_SOURCES += [ - 'nsIUpdateTimerManager.idl', + 'nsIUpdateService.idl', ] +TEST_DIRS += ['tests'] + EXTRA_COMPONENTS += [ - 'nsUpdateTimerManager.js', - 'nsUpdateTimerManager.manifest', + 'nsUpdateService.js', + 'nsUpdateService.manifest', + 'nsUpdateServiceStub.js', ] -if CONFIG['MOZ_UPDATER']: - TEST_DIRS += ['tests'] - - XPIDL_SOURCES += [ - 'nsIUpdateService.idl', - ] - - EXTRA_COMPONENTS += [ - 'nsUpdateService.js', - 'nsUpdateService.manifest', - 'nsUpdateServiceStub.js', - ] - - EXTRA_JS_MODULES += [ - 'UpdateTelemetry.jsm', - ] +EXTRA_JS_MODULES += [ + 'UpdateTelemetry.jsm', +] JAR_MANIFESTS += ['jar.mn'] + +FAIL_ON_WARNINGS = True + +with Files('**'): + BUG_COMPONENT = ('Toolkit', 'Application Update')
--- a/toolkit/mozapps/update/tests/Makefile.in +++ b/toolkit/mozapps/update/tests/Makefile.in @@ -13,18 +13,16 @@ aus-test-const_PATH := $(XPCSHELLTESTR aus-test-const_FLAGS := -Fsubstitution $(DEFINES) $(ACDEFINES) aus-test-const_TARGET := misc INSTALL_TARGETS += xpcshell-data xpcshell-data_FILES := $(filter-out $(pp_const_file),$(wildcard $(srcdir)/data/*)) xpcshell-data_DEST := $(XPCSHELLTESTROOT)/data xpcshell-data_TARGET := misc -# Android doesn't use the Mozilla updater or the toolkit update UI -ifneq (android,$(MOZ_WIDGET_TOOLKIT)) ifndef MOZ_PROFILE_GENERATE ifdef COMPILE_ENVIRONMENT INSTALL_TARGETS += xpcshell-helper xpcshell-helper_FILES := $(DIST)/bin/TestAUSHelper$(BIN_SUFFIX) xpcshell-helper_DEST := $(XPCSHELLTESTROOT)/data xpcshell-helper_TARGET := misc endif endif # Not MOZ_PROFILE_GENERATE @@ -42,29 +40,25 @@ chrome-data_TARGET := misc INI_TEST_FILES = \ TestAUSReadStrings1.ini \ TestAUSReadStrings2.ini \ TestAUSReadStrings3.ini \ $(NULL) MOZ_WINCONSOLE = 1 -endif # Not Android - include $(topsrcdir)/config/rules.mk -ifneq (android,$(MOZ_WIDGET_TOOLKIT)) # TestAUSReadStrings runs during check in the following directory with a Unicode # char in order to test bug 473417 on Windows. ifeq ($(OS_ARCH),WINNT) bug473417dir = test_bug473417-ó else bug473417dir = test_bug473417 endif check:: $(RM) -rf $(DEPTH)/_tests/updater/ && $(NSINSTALL) -D $(DEPTH)/_tests/updater/$(bug473417dir)/ for i in $(INI_TEST_FILES); do \ $(INSTALL) $(srcdir)/$$i $(DEPTH)/_tests/updater/$(bug473417dir)/; \ done $(INSTALL) $(FINAL_TARGET)/TestAUSReadStrings$(BIN_SUFFIX) $(DEPTH)/_tests/updater/$(bug473417dir)/ @$(RUN_TEST_PROGRAM) $(DEPTH)/_tests/updater/$(bug473417dir)/TestAUSReadStrings$(BIN_SUFFIX) -endif # Not Android
--- a/toolkit/mozapps/update/tests/moz.build +++ b/toolkit/mozapps/update/tests/moz.build @@ -3,47 +3,45 @@ # 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/. HAS_MISC_RULE = True XPCSHELL_TESTS_MANIFESTS += ['unit_aus_update/xpcshell.ini'] -if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': - MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini'] - XPCSHELL_TESTS_MANIFESTS += ['unit_base_updater/xpcshell.ini'] +MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini'] +XPCSHELL_TESTS_MANIFESTS += ['unit_base_updater/xpcshell.ini'] - if CONFIG['MOZ_MAINTENANCE_SERVICE']: - XPCSHELL_TESTS_MANIFESTS += ['unit_service_updater/xpcshell.ini'] +if CONFIG['MOZ_MAINTENANCE_SERVICE']: + XPCSHELL_TESTS_MANIFESTS += ['unit_service_updater/xpcshell.ini'] - SimplePrograms([ - 'TestAUSHelper', - 'TestAUSReadStrings', - ]) +SimplePrograms([ + 'TestAUSHelper', + 'TestAUSReadStrings', +]) - LOCAL_INCLUDES += [ - '/toolkit/mozapps/update', - '/toolkit/mozapps/update/common', +LOCAL_INCLUDES += [ + '/toolkit/mozapps/update', + '/toolkit/mozapps/update/common', +] + +if CONFIG['OS_ARCH'] == 'WINNT': + USE_LIBS += [ + 'updatecommon-standalone', ] - if CONFIG['OS_ARCH'] == 'WINNT': - USE_LIBS += [ - 'updatecommon-standalone', - ] - else: - USE_LIBS += [ - 'updatecommon', - ] - - if CONFIG['OS_ARCH'] == 'WINNT': - OS_LIBS += [ - 'wintrust', - 'shlwapi', - ] + OS_LIBS += [ + 'wintrust', + 'shlwapi', + ] +else: + USE_LIBS += [ + 'updatecommon', + ] for var in ('MOZ_APP_NAME', 'MOZ_APP_BASENAME', 'MOZ_APP_DISPLAYNAME', 'MOZ_APP_VENDOR', 'BIN_SUFFIX', 'MOZ_DEBUG'): DEFINES[var] = CONFIG[var] DEFINES['NS_NO_XPCOM'] = True if CONFIG['MOZ_MAINTENANCE_SERVICE']:
--- a/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini +++ b/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini @@ -1,16 +1,15 @@ ; 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/. [DEFAULT] head = head_update.js tail = -skip-if = toolkit == 'android' [canCheckForAndCanApplyUpdates.js] [urlConstruction.js] [updateCheckOnLoadOnErrorStatusText.js] [updateManagerXML.js] [remoteUpdateXML.js] [downloadAndHashCheckMar.js] [cleanupDownloadingForOlderAppVersion.js]
--- a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini +++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini @@ -4,17 +4,16 @@ ; Tests that require the updater binary. These tests should never run on Android ; which doesn't use the updater binary as other applications do and are excluded ; from running the tests in the moz.build file. [DEFAULT] head = head_update.js tail = -skip-if = toolkit == 'android' [marSuccessComplete.js] [marSuccessPartial.js] [marFailurePartial.js] [marStageSuccessComplete.js] skip-if = toolkit == 'gonk' reason = bug 820380 [marStageSuccessPartial.js]
--- a/toolkit/mozapps/update/updater/updater-xpcshell/moz.build +++ b/toolkit/mozapps/update/updater/updater-xpcshell/moz.build @@ -5,8 +5,9 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. Program('updater-xpcshell') updater_rel_path = '../' NO_DIST_INSTALL = True DEFINES['UPDATER_XPCSHELL_CERT'] = True include('../updater-common.build') +FAIL_ON_WARNINGS = True
--- a/toolkit/themes/osx/global/jar.mn +++ b/toolkit/themes/osx/global/jar.mn @@ -1,23 +1,23 @@ # 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/. toolkit.jar: % skin global classic/1.0 %skin/classic/global/ skin/classic/global/10pct_transparent_grey.png skin/classic/global/50pct_transparent_grey.png - skin/classic/global/about.css (../../windows/global/about.css) - skin/classic/global/aboutCache.css (../../windows/global/aboutCache.css) - skin/classic/global/aboutCacheEntry.css (../../windows/global/aboutCacheEntry.css) - skin/classic/global/aboutMemory.css (../../windows/global/aboutMemory.css) - skin/classic/global/aboutReader.css (../../windows/global/aboutReader.css) - skin/classic/global/aboutSupport.css (../../windows/global/aboutSupport.css) - skin/classic/global/appPicker.css (../../windows/global/appPicker.css) + skin/classic/global/about.css (../../shared/about.css) + skin/classic/global/aboutCache.css (../../shared/aboutCache.css) + skin/classic/global/aboutCacheEntry.css (../../shared/aboutCacheEntry.css) + skin/classic/global/aboutMemory.css (../../shared/aboutMemory.css) + skin/classic/global/aboutReader.css (../../shared/aboutReader.css) + skin/classic/global/aboutSupport.css (../../shared/aboutSupport.css) + skin/classic/global/appPicker.css (../../shared/appPicker.css) skin/classic/global/arrow.css skin/classic/global/autocomplete.css skin/classic/global/button.css skin/classic/global/checkbox.css skin/classic/global/colorpicker.css skin/classic/global/commonDialog.css skin/classic/global/config.css (../../shared/config.css) skin/classic/global/customizeToolbar.css
rename from toolkit/themes/windows/global/aboutCache.css rename to toolkit/themes/shared/aboutCache.css
rename from toolkit/themes/windows/global/aboutCacheEntry.css rename to toolkit/themes/shared/aboutCacheEntry.css
rename from toolkit/themes/windows/global/aboutMemory.css rename to toolkit/themes/shared/aboutMemory.css
rename from toolkit/themes/windows/global/aboutReader.css rename to toolkit/themes/shared/aboutReader.css
rename from toolkit/themes/windows/global/aboutSupport.css rename to toolkit/themes/shared/aboutSupport.css
rename from toolkit/themes/windows/global/appPicker.css rename to toolkit/themes/shared/appPicker.css
--- a/toolkit/themes/windows/global/jar.mn +++ b/toolkit/themes/windows/global/jar.mn @@ -1,21 +1,21 @@ # 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/. toolkit.jar: % skin global classic/1.0 %skin/classic/global/ - skin/classic/global/about.css - skin/classic/global/aboutCache.css - skin/classic/global/aboutCacheEntry.css - skin/classic/global/aboutMemory.css - skin/classic/global/aboutReader.css - skin/classic/global/aboutSupport.css - skin/classic/global/appPicker.css + skin/classic/global/about.css (../../shared/about.css) + skin/classic/global/aboutCache.css (../../shared/aboutCache.css) + skin/classic/global/aboutCacheEntry.css (../../shared/aboutCacheEntry.css) + skin/classic/global/aboutMemory.css (../../shared/aboutMemory.css) + skin/classic/global/aboutReader.css (../../shared/aboutReader.css) + skin/classic/global/aboutSupport.css (../../shared/aboutSupport.css) + skin/classic/global/appPicker.css (../../shared/appPicker.css) skin/classic/global/arrow.css * skin/classic/global/autocomplete.css skin/classic/global/button.css skin/classic/global/checkbox.css skin/classic/global/colorpicker.css skin/classic/global/commonDialog.css skin/classic/global/config.css (../../shared/config.css) skin/classic/global/customizeToolbar.css
--- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -18,17 +18,19 @@ #include "mozilla/IOInterposer.h" #include "mozilla/Likely.h" #include "mozilla/Poison.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "nsAppRunner.h" #include "mozilla/AppData.h" +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) #include "nsUpdateDriver.h" +#endif #include "ProfileReset.h" #ifdef MOZ_INSTRUMENT_EVENT_LOOP #include "EventTracer.h" #endif #ifdef XP_MACOSX #include "nsVersionComparator.h" @@ -3713,17 +3715,17 @@ XREMain::XRE_mainStartup(bool* aExitFlag nsAutoCString desktopStartupEnv; desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); desktopStartupEnv.Append(mDesktopStartupID); // Leak it with extreme prejudice! PR_SetEnv(ToNewCString(desktopStartupEnv)); } #endif -#if defined(USE_MOZ_UPDATER) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) // Check for and process any available updates nsCOMPtr<nsIFile> updRoot; bool persistent; rv = mDirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent, getter_AddRefs(updRoot)); // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed if (NS_FAILED(rv)) updRoot = mDirProvider.GetAppDir();
--- a/toolkit/xre/nsUpdateDriver.h +++ b/toolkit/xre/nsUpdateDriver.h @@ -3,23 +3,21 @@ /* 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/. */ #ifndef nsUpdateDriver_h__ #define nsUpdateDriver_h__ #include "nscore.h" -#ifdef MOZ_UPDATER #include "nsIUpdateService.h" #include "nsIThread.h" #include "nsCOMPtr.h" #include "nsString.h" #include "mozilla/Attributes.h" -#endif class nsIFile; #if defined(XP_WIN) #include <windows.h> typedef HANDLE ProcessType; #elif defined(XP_MACOSX) typedef pid_t ProcessType; @@ -52,17 +50,16 @@ nsresult ProcessUpdates(nsIFile *greDir, nsIFile *updRootDir, int argc, char **argv, const char *appVersion, bool restart = true, bool isOSUpdate = false, nsIFile *osApplyToDir = nullptr, ProcessType *pid = nullptr); -#ifdef MOZ_UPDATER // The implementation of the update processor handles the task of loading the // updater application for staging an update. // XXX ehsan this is living in this file in order to make use of the existing // stuff here, we might want to move it elsewhere in the future. class nsUpdateProcessor final : public nsIUpdateProcessor { public: nsUpdateProcessor(); @@ -102,11 +99,9 @@ private: void UpdateDone(); void ShutdownWatcherThread(); private: ProcessType mUpdaterPID; nsCOMPtr<nsIThread> mProcessWatcher; StagedUpdateInfo mInfo; }; -#endif - #endif // nsUpdateDriver_h__
--- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -39,16 +39,17 @@ #include "nsPrintfCString.h" #include "NativeJSContainer.h" #include "nsContentUtils.h" #include "nsIScriptError.h" #include "nsIHttpChannel.h" #include "MediaCodec.h" #include "SurfaceTexture.h" +#include "GLContextProvider.h" using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::jni; using namespace mozilla::widget; AndroidBridge* AndroidBridge::sBridge = nullptr; pthread_t AndroidBridge::sJavaUiThread = -1; @@ -816,16 +817,18 @@ AndroidBridge::OpenGraphicsLibraries() // Android 2.3+ (API level 9) handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); if (handle) { ANativeWindow_fromSurface = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurface"); ANativeWindow_release = (void (*)(void*))dlsym(handle, "ANativeWindow_release"); ANativeWindow_setBuffersGeometry = (int (*)(void*, int, int, int)) dlsym(handle, "ANativeWindow_setBuffersGeometry"); ANativeWindow_lock = (int (*)(void*, void*, void*)) dlsym(handle, "ANativeWindow_lock"); ANativeWindow_unlockAndPost = (int (*)(void*))dlsym(handle, "ANativeWindow_unlockAndPost"); + ANativeWindow_getWidth = (int (*)(void*))dlsym(handle, "ANativeWindow_getWidth"); + ANativeWindow_getHeight = (int (*)(void*))dlsym(handle, "ANativeWindow_getHeight"); // This is only available in Honeycomb and ICS. It was removed in Jelly Bean ANativeWindow_fromSurfaceTexture = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurfaceTexture"); mHasNativeWindowAccess = ANativeWindow_fromSurface && ANativeWindow_release && ANativeWindow_lock && ANativeWindow_unlockAndPost; ALOG_BRIDGE("Successfully opened libandroid.so, have native window access? %d", mHasNativeWindowAccess); } @@ -1270,16 +1273,26 @@ AndroidBridge::ReleaseNativeWindow(void if (mHasNativeWindowAccess) ANativeWindow_release(window); // XXX: we don't ref the pointer we get from the fallback (GetNativeSurface), so we // have nothing to do here. We should probably ref it. } +IntSize +AndroidBridge::GetNativeWindowSize(void* window) +{ + if (!window || !ANativeWindow_getWidth || !ANativeWindow_getHeight) { + return IntSize(0, 0); + } + + return IntSize(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window)); +} + void* AndroidBridge::AcquireNativeWindowFromSurfaceTexture(JNIEnv* aEnv, jobject aSurfaceTexture) { OpenGraphicsLibraries(); if (mHasNativeWindowAccess && ANativeWindow_fromSurfaceTexture) return ANativeWindow_fromSurfaceTexture(aEnv, aSurfaceTexture); @@ -1503,17 +1516,19 @@ void AndroidBridge::SyncFrameMetrics(con aFixedLayerMargins.bottom = viewTransform->FixedLayerMarginBottom(); aFixedLayerMargins.left = viewTransform->FixedLayerMarginLeft(); aOffset.x = viewTransform->OffsetX(); aOffset.y = viewTransform->OffsetY(); } AndroidBridge::AndroidBridge() - : mLayerClient(nullptr) + : mLayerClient(nullptr), + mPresentationWindow(nullptr), + mPresentationSurface(nullptr) { } AndroidBridge::~AndroidBridge() { } /* Implementation file */ @@ -2031,16 +2046,61 @@ AndroidBridge::RunDelayedUiThreadTasks() Task* task = nextTask->GetTask(); delete nextTask; task->Run(); } return -1; } +void* +AndroidBridge::GetPresentationWindow() +{ + return mPresentationWindow; +} + +void +AndroidBridge::SetPresentationWindow(void* aPresentationWindow) +{ + if (mPresentationWindow) { + const bool wasAlreadyPaused = nsWindow::IsCompositionPaused(); + if (!wasAlreadyPaused) { + nsWindow::SchedulePauseComposition(); + } + + mPresentationWindow = aPresentationWindow; + if (mPresentationSurface) { + // destroy the egl surface! + // The compositor is paused so it should be okay to destroy + // the surface here. + mozilla::gl::GLContextProvider::DestroyEGLSurface(mPresentationSurface); + mPresentationSurface = nullptr; + } + + if (!wasAlreadyPaused) { + nsWindow::ScheduleResumeComposition(); + } + } + else { + mPresentationWindow = aPresentationWindow; + } +} + +EGLSurface +AndroidBridge::GetPresentationSurface() +{ + return mPresentationSurface; +} + +void +AndroidBridge::SetPresentationSurface(EGLSurface aPresentationSurface) +{ + mPresentationSurface = aPresentationSurface; +} + Object::LocalRef AndroidBridge::ChannelCreate(Object::Param stream) { JNIEnv* const env = GetJNIForThread(); auto rv = Object::LocalRef::Adopt(env, env->CallStaticObjectMethod( sBridge->jReadableByteChannel, sBridge->jChannelCreate, stream.Get())); HandleUncaughtException(env); return rv; }
--- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -16,16 +16,17 @@ #include "GeneratedJNIWrappers.h" #include "AndroidJavaWrappers.h" #include "nsIMutableArray.h" #include "nsIMIMEInfo.h" #include "nsColor.h" #include "gfxRect.h" +#include "mozilla/gfx/Point.h" #include "nsIAndroidBridge.h" #include "nsIMobileMessageCallback.h" #include "mozilla/Likely.h" #include "mozilla/StaticPtr.h" #include "mozilla/TimeStamp.h" #include "mozilla/Types.h" @@ -259,16 +260,17 @@ public: WINDOW_FORMAT_RGBX_8888 = 2, WINDOW_FORMAT_RGB_565 = 4 }; bool HasNativeWindowAccess(); void *AcquireNativeWindow(JNIEnv* aEnv, jobject aSurface); void ReleaseNativeWindow(void *window); + mozilla::gfx::IntSize GetNativeWindowSize(void* window); void *AcquireNativeWindowFromSurfaceTexture(JNIEnv* aEnv, jobject aSurface); void ReleaseNativeWindowForSurfaceTexture(void *window); bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride); bool UnlockWindow(void *window); void HandleGeckoMessage(JSContext* cx, JS::HandleObject message); @@ -421,29 +423,40 @@ protected: void* (*ANativeWindow_fromSurface)(JNIEnv *env, jobject surface); void* (*ANativeWindow_fromSurfaceTexture)(JNIEnv *env, jobject surfaceTexture); void (*ANativeWindow_release)(void *window); int (*ANativeWindow_setBuffersGeometry)(void *window, int width, int height, int format); int (* ANativeWindow_lock)(void *window, void *outBuffer, void *inOutDirtyBounds); int (* ANativeWindow_unlockAndPost)(void *window); + int (* ANativeWindow_getWidth)(void * window); + int (* ANativeWindow_getHeight)(void * window); int (* Surface_lock)(void* surface, void* surfaceInfo, void* region, bool block); int (* Surface_unlockAndPost)(void* surface); void (* Region_constructor)(void* region); void (* Region_set)(void* region, void* rect); private: // This will always be accessed from one thread (the Java UI thread), // so we don't need to do locking to touch it. nsTArray<DelayedTask*> mDelayedTaskQueue; public: void PostTaskToUiThread(Task* aTask, int aDelayMs); int64_t RunDelayedUiThreadTasks(); + + void* GetPresentationWindow(); + void SetPresentationWindow(void* aPresentationWindow); + + EGLSurface GetPresentationSurface(); + void SetPresentationSurface(EGLSurface aPresentationSurface); +private: + void* mPresentationWindow; + EGLSurface mPresentationSurface; }; class AutoJNIClass { private: JNIEnv* const mEnv; const jclass mClass; public:
--- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -807,16 +807,37 @@ Java_org_mozilla_gecko_GeckoAppShell_get cleanup: AndroidBridge::Bridge()->UnlockWindow(window); AndroidBridge::Bridge()->ReleaseNativeWindow(window); return surfaceBits; } NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface(JNIEnv* jenv, jclass, jobject surface) +{ + if (surface != NULL) { + void* window = AndroidBridge::Bridge()->AcquireNativeWindow(jenv, surface); + if (window) { + AndroidBridge::Bridge()->SetPresentationWindow(window); + } + } +} + +NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface(JNIEnv* jenv, jclass, jobject surface) +{ + void* window = AndroidBridge::Bridge()->GetPresentationWindow(); + if (window) { + AndroidBridge::Bridge()->SetPresentationWindow(nullptr); + AndroidBridge::Bridge()->ReleaseNativeWindow(window); + } +} + +NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden(JNIEnv* jenv, jclass, jobject view) { class ExitFullScreenRunnable : public nsRunnable { public: ExitFullScreenRunnable(jobject view) : mView(view) {} NS_IMETHODIMP Run() { JNIEnv* env = AndroidBridge::GetJNIEnv();
--- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -2449,16 +2449,39 @@ nsWindow::SetCompositor(mozilla::layers: void nsWindow::ScheduleComposite() { if (sCompositorParent) { sCompositorParent->ScheduleRenderOnCompositorThread(); } } +bool +nsWindow::IsCompositionPaused() +{ + return sCompositorPaused; +} + +void +nsWindow::SchedulePauseComposition() +{ + if (sCompositorParent) { + sCompositorParent->SchedulePauseOnCompositorThread(); + sCompositorPaused = true; + } +} + +void +nsWindow::ScheduleResumeComposition() +{ + if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread()) { + sCompositorPaused = false; + } +} + void nsWindow::ScheduleResumeComposition(int width, int height) { if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread(width, height)) { sCompositorPaused = false; } }
--- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -151,17 +151,20 @@ public: virtual void DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect); virtual void DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect); virtual mozilla::layers::CompositorParent* NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) override; static void SetCompositor(mozilla::layers::LayerManager* aLayerManager, mozilla::layers::CompositorParent* aCompositorParent, mozilla::layers::CompositorChild* aCompositorChild); + static bool IsCompositionPaused(); static void ScheduleComposite(); + static void SchedulePauseComposition(); + static void ScheduleResumeComposition(); static void ScheduleResumeComposition(int width, int height); static void ForceIsFirstPaint(); static float ComputeRenderIntegrity(); static mozilla::layers::APZCTreeManager* GetAPZCTreeManager(); /* RootLayerTreeId() can only be called when GetAPZCTreeManager() returns non-null */ static uint64_t RootLayerTreeId(); virtual bool WidgetPaintsBackground();