author | Ally Naaktgeboren <ally@mozilla.com> |
Thu, 07 Mar 2013 15:38:03 -0800 | |
changeset 124172 | a1586c3b0da81c55267c86c59b33d4a0dbd3ef4f |
parent 124171 | 08096dc62f37d048a44d70b0462aa857f8b6c329 |
child 124173 | a8088accbcbe8bcd1437dd02f7cd26568d41f483 |
push id | 24408 |
push user | ryanvm@gmail.com |
push date | Fri, 08 Mar 2013 04:58:11 +0000 |
treeherder | mozilla-central@cb432984d5ce [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mbrubeck |
bugs | 794028 |
milestone | 22.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/browser/metro/base/content/TopSites.js +++ b/browser/metro/base/content/TopSites.js @@ -1,17 +1,18 @@ // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- /* 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'; let prefs = Components.classes["@mozilla.org/preferences-service;1"]. getService(Components.interfaces.nsIPrefBranch); +Cu.import("resource://gre/modules/PageThumbs.jsm"); + // singleton to provide data-level functionality to the views let TopSites = { pinSite: function(aId, aSlotIndex) { Util.dumpLn("TopSites.pinSite: " + aId + ", (TODO)"); // FIXME: implementation needed return true; // operation was successful }, unpinSite: function(aId) { @@ -26,27 +27,34 @@ let TopSites = { }, restoreSite: function(aId) { Util.dumpLn("TopSites.restoreSite: " + aId + ", (TODO)"); // FIXME: implementation needed return true; // operation was successful } }; -function TopSitesView(aGrid, maxSites) { +// The value of useThumbs should not be changed over the lifetime of +// the object. +function TopSitesView(aGrid, aMaxSites, aUseThumbnails) { this._set = aGrid; this._set.controller = this; - this._topSitesMax = maxSites; + this._topSitesMax = aMaxSites; + this._useThumbs = aUseThumbnails; // handle selectionchange DOM events from the grid/tile group this._set.addEventListener("context-action", this, false); let history = Cc["@mozilla.org/browser/nav-history-service;1"]. getService(Ci.nsINavHistoryService); history.addObserver(this, false); + if (this._useThumbs) { + PageThumbs.addExpirationFilter(this); + Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false); + } } TopSitesView.prototype = { _set:null, _topSitesMax: null, handleItemClick: function tabview_handleItemClick(aItem) { let url = aItem.getAttribute("value"); @@ -142,28 +150,55 @@ TopSitesView.prototype = { } else { supportedActions.push('pin'); } let item = this._set.appendItem(title, uri); item.setAttribute("iconURI", node.icon); item.setAttribute("data-itemid", node[identifier]); // here is where we could add verbs based on pinned etc. state item.setAttribute("data-contextactions", supportedActions.join(',')); + + if (this._useThumbs) { + let thumbnail = PageThumbs.getThumbnailURL(uri); + let cssthumbnail = 'url("'+thumbnail+'")'; + item.backgroundImage = cssthumbnail; + } } rootNode.containerOpen = false; }, + forceReloadOfThumbnail: function forceReloadOfThumbnail(url) { + let nodes = this._set.querySelectorAll('richgriditem[value="'+url+'"]'); + for (let item of nodes) { + item.refreshBackgroundImage(); + } + }, + filterForThumbnailExpiration: function filterForThumbnailExpiration(aCallback) { + aCallback([item.getAttribute("value") for (item of this._set.children)]); + }, + isFirstRun: function isFirstRun() { return prefs.getBoolPref("browser.firstrun.show.localepicker"); }, destruct: function destruct() { - // remove the observers here + if (this._useThumbs) { + Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail"); + PageThumbs.removeExpirationFilter(this); + } }, + // nsIObservers + observe: function (aSubject, aTopic, aState) { + switch(aTopic) { + case "Metro:RefreshTopsiteThumbnail": + this.forceReloadOfThumbnail(aState); + break; + } + }, // nsINavHistoryObserver onBeginUpdateBatch: function() { }, onEndUpdateBatch: function() { }, @@ -197,17 +232,17 @@ TopSitesView.prototype = { }; let TopSitesStartView = { _view: null, get _grid() { return document.getElementById("start-topsites-grid"); }, init: function init() { - this._view = new TopSitesView(this._grid, 9); + this._view = new TopSitesView(this._grid, 9, true); if (this._view.isFirstRun()) { let topsitesVbox = document.getElementById("start-topsites"); topsitesVbox.setAttribute("hidden", "true"); } this._view.populateGrid(); }, uninit: function uninit() {
--- a/browser/metro/base/content/bindings/grid.xml +++ b/browser/metro/base/content/bindings/grid.xml @@ -471,38 +471,42 @@ </body> </method> </implementation> </binding> <binding id="richgrid-item"> <content> - <xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content"> - <xul:hbox class="richgrid-icon-container"> + <xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content" xbl:inherits="customImagePresent"> + <xul:hbox class="richgrid-icon-container" xbl:inherits="customImagePresent"> <xul:box class="richgrid-icon-box"><xul:image xbl:inherits="src=iconURI"/></xul:box> <xul:box flex="1" /> </xul:hbox> - <xul:description xbl:inherits="value=label" crop="end"/> + <xul:description class="richgrid-item-desc" xbl:inherits="value=label" crop="end"/> </xul:vbox> </content> <implementation> <property name="_box" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item');"/> <property name="color" onset="this._color = val; this.setColor();" onget="return this._color;"/> + <property name="backgroundimage" + onset="this._backgroundimage = val; this.setBackgroundImage();" + onget="return this._backgroundimage;" /> <property name="selected" onget="return this.getAttribute('selected') == 'true';" onset="this.setAttribute('selected', val);"/> <constructor> <![CDATA[ // Bindings don't get bound until the item is displayed, - // so we have to reset the background color when we get + // so we have to reset the background color/image when we get // created. this.setColor(); + this.setBackgroundImage(); ]]> </constructor> <property name="control"> <getter><![CDATA[ var parent = this.parentNode; while (parent) { if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement) @@ -521,16 +525,38 @@ this._box.style.backgroundColor = this.color; } else { this._box.parentNode.removeAttribute("customColorPresent"); } ]]> </body> </method> + <method name="setBackgroundImage"> + <body> + <![CDATA[ + if (this.backgroundImage != undefined) { + this._box.parentNode.setAttribute("customImagePresent", "true"); + this._box.style.backgroundImage = this.backgroundImage; + } else { + this._box.parentNode.removeAttribute("customImagePresent"); + this._box.style.removeProperty("background-image"); + } + ]]> + </body> + </method> + <method name="refreshBackgroundImage"> + <body><![CDATA[ + if (this.backgroundImage) { + this._box.style.removeProperty("background-image"); + this._box.style.setProperty("background-image", this.backgroundImage); + } + ]]></body> + </method> + <field name="_contextActions">null</field> <property name="contextActions"> <getter> <![CDATA[ if(!this._contextActions) { this._contextActions = new Set(); let actionSet = this._contextActions; let actions = this.getAttribute("data-contextactions");
--- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -1,13 +1,15 @@ // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- /* 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/. */ +Cu.import("resource://gre/modules/PageThumbs.jsm"); + /** * Constants */ // BrowserUI.update(state) constants. Currently passed in // but update doesn't pay attention to them. Can we remove? const TOOLBARSTATE_LOADING = 1; const TOOLBARSTATE_LOADED = 2; @@ -78,16 +80,17 @@ var BrowserUI = { init: function() { // listen content messages messageManager.addMessageListener("DOMTitleChanged", this); messageManager.addMessageListener("DOMWillOpenModalDialog", this); messageManager.addMessageListener("DOMWindowClose", this); messageManager.addMessageListener("Browser:OpenURI", this); messageManager.addMessageListener("Browser:SaveAs:Return", this); + messageManager.addMessageListener("Content:StateChange", this); // listening escape to dismiss dialog on VK_ESCAPE window.addEventListener("keypress", this, true); window.addEventListener("MozPrecisePointer", this, true); window.addEventListener("MozImprecisePointer", this, true); Services.prefs.addObserver("browser.tabs.tabsOnly", this, false); @@ -97,16 +100,17 @@ var BrowserUI = { ContextUI.init(); StartUI.init(); PanelUI.init(); IdentityUI.init(); if (Browser.getHomePage() === "about:start") { StartUI.show(); } FlyoutPanelsUI.init(); + PageThumbs.init(); // show the right toolbars, awesomescreen, etc for the os viewstate BrowserUI._adjustDOMforViewState(); // We can delay some initialization until after startup. We wait until // the first page is shown, then dispatch a UIReadyDelayed event. messageManager.addMessageListener("pageshow", function() { if (getBrowser().currentURI.spec == "about:blank") @@ -190,16 +194,18 @@ var BrowserUI = { uninit: function() { messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps); PanelUI.uninit(); StartUI.uninit(); Downloads.uninit(); SettingsCharm.uninit(); + messageManager.removeMessageListener("Content:StateChange", this); + PageThumbs.uninit(); }, /********************************* * Content visibility */ get isContentShowing() { @@ -800,21 +806,99 @@ var BrowserUI = { // XXX this and content's sender are a little warped case "Browser:OpenURI": let referrerURI = null; if (json.referrer) referrerURI = Services.io.newURI(json.referrer, null, null); //Browser.addTab(json.uri, json.bringFront, Browser.selectedTab, { referrerURI: referrerURI }); this.goToURI(json.uri); break; + case "Content:StateChange": + let currBrowser = Browser.selectedBrowser; + if (this.shouldCaptureThumbnails(currBrowser)) { + PageThumbs.captureAndStore(currBrowser); + let currPage = currBrowser.currentURI.spec; + Services.obs.notifyObservers(null, "Metro:RefreshTopsiteThumbnail", currPage); + } + break; } return {}; }, + // Private Browsing is not supported on metro at this time, when it is added + // this function must be updated to skip capturing those pages + shouldCaptureThumbnails: function shouldCaptureThumbnails(aBrowser) { + // Capture only if it's the currently selected tab. + if (aBrowser != Browser.selectedBrowser) { + return false; + } + // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as + // that currently regresses Talos SVG tests. + let doc = aBrowser.contentDocument; + if (doc instanceof SVGDocument || doc instanceof XMLDocument) { + return false; + } + + // There's no point in taking screenshot of loading pages. + if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) { + return false; + } + + // Don't take screenshots of about: pages. + if (aBrowser.currentURI.schemeIs("about")) { + return false; + } + + // No valid document channel. We shouldn't take a screenshot. + let channel = aBrowser.docShell.currentDocumentChannel; + if (!channel) { + return false; + } + + // Don't take screenshots of internally redirecting about: pages. + // This includes error pages. + let uri = channel.originalURI; + if (uri.schemeIs("about")) { + return false; + } + + // http checks + let httpChannel; + try { + httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); + } catch (e) { /* Not an HTTP channel. */ } + + if (httpChannel) { + // Continue only if we have a 2xx status code. + try { + if (Math.floor(httpChannel.responseStatus / 100) != 2) { + return false; + } + } catch (e) { + // Can't get response information from the httpChannel + // because mResponseHead is not available. + return false; + } + + // Cache-Control: no-store. + if (httpChannel.isNoStoreResponse()) { + return false; + } + + // Desktop has a pref that allows users to override this. We do not + // support that pref currently + if (uri.schemeIs("https")) { + return false; + } + } + + return true; + }, + supportsCommand : function(cmd) { var isSupported = false; switch (cmd) { case "cmd_back": case "cmd_forward": case "cmd_reload": case "cmd_forceReload": case "cmd_stop":
--- a/browser/metro/theme/platform.css +++ b/browser/metro/theme/platform.css @@ -451,17 +451,17 @@ richgriditem { richgriditem .richgrid-item-content { border: @metro_border_thin@ solid @tile_border_color@; box-shadow: 0 0 @metro_spacing_snormal@ rgba(0, 0, 0, 0.1); -moz-box-sizing: border-box; padding: 10px 8px 6px 8px; } -richgriditem:not([customColorPresent]) .richgrid-item-content { +.richgrid-item-content { background: #fff; } richgriditem[selected="true"] .richgrid-item-content { border: @metro_border_xthick@ solid @selected_color@; padding: @metro_spacing_xxsmall@; } @@ -470,32 +470,59 @@ richgriditem .richgrid-icon-container { } richgriditem .richgrid-icon-box { padding: 4px; background: #fff; opacity: 1.0; } -richgriditem[customColorPresent="true"] description { + +richgriditem[customColorPresent="true"] { color: #f1f1f1; } +richgriditem[customImagePresent] { + color: #1a1a1a; +} + richgriditem[customColorPresent="true"] .richgrid-icon-box { opacity: 0.8; background-color: #fff; } -richgriditem description { +.richgrid-item-content[customImagePresent] { + height: 120px; + width: 200px; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + -moz-box-pack: end; + padding: 0px; +} + +/* hide icon if there is an image background */ +.richgrid-icon-container[customImagePresent] { + visibility: collapse; +} + +.richgrid-item-desc { width: @tile_width@; font-size: @metro_font_normal@; margin-left: 0px !important; padding-left: 0px !important; } +.richgrid-item-content[customImagePresent] > .richgrid-item-desc { + background: hsla(0,2%,98%,.95); + /*margin-bottom: 0px; + margin-right: 0px;*/ + margin: 0px; +} + richgriditem image { width: 24px; height: 24px; list-style-image: url("chrome://browser/skin/images/identity-icons-generic.png"); } /* Dialogs ----------------------------------------------------------------- */