merge fx-team to mozilla-central

This commit is contained in:
Carsten "Tomcat" Book 2014-01-28 13:45:03 +01:00
Родитель d46a808c6c 2fde44feed
Коммит bf8663e325
60 изменённых файлов: 1122 добавлений и 387 удалений

Просмотреть файл

@ -271,7 +271,8 @@ var gPluginHandler = {
if (eventType == "PluginRemoved") {
let doc = event.target;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
this._setPluginNotificationIcon(browser);
if (browser)
this._setPluginNotificationIcon(browser);
return;
}
@ -301,6 +302,8 @@ var gPluginHandler = {
let shouldShowNotification = false;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
if (!browser)
return;
switch (eventType) {
case "PluginCrashed":

Просмотреть файл

@ -832,12 +832,11 @@
<toolbaritem id="PanelUI-button"
class="chromeclass-toolbar-additional"
removable="false"
title="&appmenu.title;">
removable="false">
<toolbarbutton id="PanelUI-menu-button"
class="toolbarbutton-1"
label="&brandShortName;"
tooltiptext="&appmenu.title;"/>
tooltiptext="&appmenu.tooltip;"/>
</toolbaritem>
<hbox id="window-controls" hidden="true" pack="end" skipintoolbarset="true"

Просмотреть файл

@ -5,7 +5,7 @@
<hbox id="customization-container" flex="1" hidden="true">
<vbox flex="1" id="customization-palette-container">
<label id="customization-header">
&customizeMode.menuAndToolbars.header;
&customizeMode.menuAndToolbars.header2;
</label>
<hbox id="customization-empty" hidden="true">
<label>&customizeMode.menuAndToolbars.empty;</label>

Просмотреть файл

@ -1411,10 +1411,13 @@ CustomizeMode.prototype = {
_setGridDragActive: function(aDragOverNode, aDraggedItem, aValue) {
let targetArea = this._getCustomizableParent(aDragOverNode);
let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItem.id);
let originArea = this._getCustomizableParent(draggedWrapper);
let positionManager = DragPositionManager.getManagerForArea(targetArea);
let draggedSize = this._getDragItemSize(aDragOverNode, aDraggedItem);
let isWide = aDraggedItem.classList.contains(CustomizableUI.WIDE_PANEL_CLASS);
positionManager.insertPlaceholder(targetArea, aDragOverNode, isWide, draggedSize);
positionManager.insertPlaceholder(targetArea, aDragOverNode, isWide, draggedSize,
originArea == targetArea);
},
_getDragItemSize: function(aDragOverNode, aDraggedItem) {

Просмотреть файл

@ -126,7 +126,7 @@ AreaPositionManager.prototype = {
* they would have if we had inserted something before aBefore. We use CSS
* transforms for this, which are CSS transitioned.
*/
insertPlaceholder: function(aContainer, aBefore, aWide, aSize) {
insertPlaceholder: function(aContainer, aBefore, aWide, aSize, aIsFromThisArea) {
let isShifted = false;
let shiftDown = aWide;
for (let child of aContainer.children) {
@ -157,7 +157,7 @@ AreaPositionManager.prototype = {
if (this.__moveDown) {
shiftDown = true;
}
if (!this._lastPlaceholderInsertion) {
if (aIsFromThisArea && !this._lastPlaceholderInsertion) {
child.setAttribute("notransition", "true");
}
// Determine the CSS transform based on the next node:
@ -167,7 +167,8 @@ AreaPositionManager.prototype = {
child.style.transform = "";
}
}
if (aContainer.lastChild && !this._lastPlaceholderInsertion) {
if (aContainer.lastChild && aIsFromThisArea &&
!this._lastPlaceholderInsertion) {
// Flush layout:
aContainer.lastChild.getBoundingClientRect();
// then remove all the [notransition]

Просмотреть файл

@ -15,7 +15,7 @@
inside the private browsing mode -->
<!ENTITY mainWindow.titlePrivateBrowsingSuffix "(Private Browsing)">
<!ENTITY appmenu.title "Customize and Control &brandFullName;">
<!ENTITY appmenu.tooltip "Open menu">
<!ENTITY navbarOverflow.label "More tools…">
<!-- Tab context menu -->
@ -674,7 +674,7 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
<!ENTITY customizeMode.menuAndToolbars.label "Menu and toolbars">
<!ENTITY customizeMode.menuAndToolbars.header "More Tools to Add to the Menu and Toolbar">
<!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
<!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
<!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
<!ENTITY customizeMode.restoreDefaults "Restore Defaults">

Просмотреть файл

@ -123,6 +123,9 @@ var Appbar = {
onMenuButton: function(aEvent) {
let typesArray = [];
if (BrowserUI.isPrivateBrowsingEnabled) {
typesArray.push("private-browsing");
}
if (!BrowserUI.isStartTabVisible) {
typesArray.push("find-in-page");
if (ContextCommands.getPageSource())

Просмотреть файл

@ -86,6 +86,8 @@ var BrowserUI = {
Services.prefs.addObserver(debugServerStateChanged, this, false);
Services.prefs.addObserver(debugServerPortChanged, this, false);
Services.prefs.addObserver("app.crashreporter.autosubmit", this, false);
Services.prefs.addObserver("metro.private_browsing.enabled", this, false);
this.updatePrivateBrowsingUI();
Services.obs.addObserver(this, "handle-xul-text-link", false);
@ -188,6 +190,12 @@ var BrowserUI = {
messageManager.removeMessageListener("Content:StateChange", this);
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
Services.prefs.removeObserver(debugServerStateChanged, this);
Services.prefs.removeObserver(debugServerPortChanged, this);
Services.prefs.removeObserver("app.crashreporter.autosubmit", this);
Services.prefs.removeObserver("metro.private_browsing.enabled", this);
Services.obs.removeObserver(this, "handle-xul-text-link");
PanelUI.uninit();
@ -411,9 +419,26 @@ var BrowserUI = {
* Open a new tab in the foreground in response to a user action.
* See Browser.addTab for more documentation.
*/
addAndShowTab: function (aURI, aOwner) {
addAndShowTab: function (aURI, aOwner, aParams) {
ContextUI.peekTabs(kForegroundTabAnimationDelay);
return Browser.addTab(aURI || kStartURI, true, aOwner);
return Browser.addTab(aURI || kStartURI, true, aOwner, aParams);
},
addAndShowPrivateTab: function (aURI, aOwner) {
return this.addAndShowTab(aURI, aOwner, { private: true });
},
get isPrivateBrowsingEnabled() {
return Services.prefs.getBoolPref("metro.private_browsing.enabled");
},
updatePrivateBrowsingUI: function () {
let command = document.getElementById("cmd_newPrivateTab");
if (this.isPrivateBrowsingEnabled) {
command.removeAttribute("disabled");
} else {
command.setAttribute("disabled", "true");
}
},
/**
@ -609,8 +634,9 @@ var BrowserUI = {
BrowserUI.submitLastCrashReportOrShowPrompt;
#endif
break;
case "metro.private_browsing.enabled":
this.updatePrivateBrowsingUI();
break;
}
break;
}
@ -848,29 +874,33 @@ var BrowserUI = {
referrerURI = Services.io.newURI(json.referrer, null, null);
this.goToURI(json.uri);
break;
case "Content:StateChange":
let currBrowser = Browser.selectedBrowser;
if (this.shouldCaptureThumbnails(currBrowser)) {
PageThumbs.captureAndStore(currBrowser);
let currPage = currBrowser.currentURI.spec;
case "Content:StateChange": {
let tab = Browser.selectedTab;
if (this.shouldCaptureThumbnails(tab)) {
PageThumbs.captureAndStore(tab.browser);
let currPage = tab.browser.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) {
shouldCaptureThumbnails: function shouldCaptureThumbnails(aTab) {
// Capture only if it's the currently selected tab.
if (aBrowser != Browser.selectedBrowser) {
if (aTab != Browser.selectedTab) {
return false;
}
// Skip private tabs
if (aTab.isPrivate) {
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;
let browser = aTab.browser;
let doc = browser.contentDocument;
if (doc instanceof SVGDocument || doc instanceof XMLDocument) {
return false;
}
@ -883,17 +913,17 @@ var BrowserUI = {
return false;
}
// There's no point in taking screenshot of loading pages.
if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
if (browser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
return false;
}
// Don't take screenshots of about: pages.
if (aBrowser.currentURI.schemeIs("about")) {
if (browser.currentURI.schemeIs("about")) {
return false;
}
// No valid document channel. We shouldn't take a screenshot.
let channel = aBrowser.docShell.currentDocumentChannel;
let channel = browser.docShell.currentDocumentChannel;
if (!channel) {
return false;
}

Просмотреть файл

@ -476,6 +476,7 @@ var Browser = {
* is closed, we will return to a parent or "sibling" tab if possible.
* @param aParams Object (optional) with optional properties:
* index: Number specifying where in the tab list to insert the new tab.
* private: If true, the new tab should be have Private Browsing active.
* flags, postData, charset, referrerURI: See loadURIWithFlags.
*/
addTab: function browser_addTab(aURI, aBringFront, aOwner, aParams) {
@ -1255,6 +1256,13 @@ function Tab(aURI, aParams, aOwner) {
this._eventDeferred = null;
this._updateThumbnailTimeout = null;
this._private = false;
if ("private" in aParams) {
this._private = aParams.private;
} else if (aOwner) {
this._private = aOwner.private;
}
this.owner = aOwner || null;
// Set to 0 since new tabs that have not been viewed yet are good tabs to
@ -1282,6 +1290,10 @@ Tab.prototype = {
return this._chromeTab;
},
get isPrivate() {
return this._private;
},
get pageShowPromise() {
return this._eventDeferred ? this._eventDeferred.promise : null;
},
@ -1307,6 +1319,10 @@ Tab.prototype = {
this._eventDeferred = Promise.defer();
this._chromeTab = Elements.tabList.addTab(aParams.index);
if (this.isPrivate) {
this._chromeTab.setAttribute("private", "true");
}
this._id = Browser.createTabId();
let browser = this._createBrowser(aURI, null);
@ -1461,6 +1477,11 @@ Tab.prototype = {
// let the content area manager know about this browser.
ContentAreaObserver.onBrowserCreated(browser);
if (this.isPrivate) {
let ctx = browser.docShell.QueryInterface(Ci.nsILoadContext);
ctx.usePrivateBrowsing = true;
}
// stop about:blank from loading
browser.stop();
@ -1485,7 +1506,9 @@ Tab.prototype = {
},
updateThumbnail: function updateThumbnail() {
PageThumbs.captureToCanvas(this.browser.contentWindow, this._chromeTab.thumbnailCanvas);
if (!this.isPrivate) {
PageThumbs.captureToCanvas(this.browser.contentWindow, this._chromeTab.thumbnailCanvas);
}
},
updateFavicon: function updateFavicon() {

Просмотреть файл

@ -6,7 +6,7 @@
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/bindings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/cssthrobber.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
<?xml-stylesheet href="chrome://branding/content/metro-about.css" type="text/css"?>
@ -85,6 +85,7 @@
#ifdef MOZ_SERVICES_SYNC
<command id="cmd_remoteTabs" oncommand="CommandUpdater.doCommand(this.id);"/>
#endif
<command id="cmd_newPrivateTab" oncommand="BrowserUI.addAndShowPrivateTab();"/>
<!-- misc -->
<command id="cmd_close" oncommand="CommandUpdater.doCommand(this.id);"/>
@ -162,6 +163,7 @@ Desktop browser's sync prefs.
<key id="key_closeTab" key="&closeTab.key;" modifiers="accel" command="cmd_closeTab"/>
<key id="key_closeTab2" keycode="VK_F4" modifiers="accel" command="cmd_closeTab"/>
<key id="key_undoCloseTab" key="&newTab.key;" modifiers="accel,shift" command="cmd_undoCloseTab"/>
<key id="key_newPrivateTab" key="&newPrivateTab.key;" modifiers="accel,shift" command="cmd_newPrivateTab"/>
<!-- tab selection -->
<key id="key_nextTab" oncommand="BrowserUI.selectNextTab();" keycode="VK_TAB" modifiers="accel"/>
@ -400,28 +402,34 @@ Desktop browser's sync prefs.
<spacer flex="1"/>
</hbox>
<hbox id="checkingForUpdates" align="center">
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
<image class="update-throbber"/>
<description flex="1">&update.checkingForUpdates;</description>
</hbox>
<hbox id="checkingAddonCompat" align="center">
<image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
<image class="update-throbber"/>
<description flex="1">&update.checkingAddonCompat;</description>
</hbox>
<hbox id="downloading" align="center">
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
<image class="update-throbber"/>
<description flex="1">
&update.downloading.start;<html:span id="downloadStatus"></html:span>&update.downloading.end;
</description>
</hbox>
<hbox id="applying" align="center">
<image class="update-throbber"/><label>&update.applying;</label>
<image class="update-throbber"/>
<description flex="1">&update.applying;</description>
</hbox>
<hbox id="downloadFailed" align="center">
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
</hbox>
<hbox id="adminDisabled" align="center">
<label>&update.adminDisabled;</label>
<label linewrap="true">&update.adminDisabled;</label>
</hbox>
<hbox id="noUpdatesFound" align="center">
<label>&update.noUpdatesFound;</label>
<label linewrap="true">&update.noUpdatesFound;</label>
</hbox>
<hbox id="otherInstanceHandlingUpdates" align="center">
<label>&update.otherInstanceHandlingUpdates;</label>
<label linewrap="true">&update.otherInstanceHandlingUpdates;</label>
</hbox>
<hbox id="manualUpdate" align="center">
<label>&update.manual.start;</label><label id="manualLink" linewrap="true" class="text-link"/><label>&update.manual.end;</label>
@ -807,6 +815,9 @@ Desktop browser's sync prefs.
</richlistitem>
<!-- standard buttons -->
<richlistitem id="context-newprivatetab" type="private-browsing" onclick="BrowserUI.addAndShowPrivateTab()">
<label value="&newPrivateTab.label;"/>
</richlistitem>
<richlistitem id="context-findinpage" type="find-in-page" onclick="ContextCommands.findInPage();">
<label value="&appbarFindInPage2.label;"/>
</richlistitem>

Просмотреть файл

@ -506,7 +506,7 @@ appUpdater.prototype =
*/
setupDownloadingUI: function() {
this.downloadStatus = document.getElementById("downloadStatus");
this.downloadStatus.value =
this.downloadStatus.textContent =
DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
this.selectPanel("downloading");
this.aus.addDownloadListener(this);
@ -598,7 +598,7 @@ appUpdater.prototype =
* See nsIProgressEventSink.idl
*/
onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
this.downloadStatus.value =
this.downloadStatus.textContent =
DownloadUtils.getTransferTotal(aProgress, aProgressMax);
},

Просмотреть файл

@ -26,8 +26,7 @@
<meta name="viewport" content="width=device-width; user-scalable=false;" />
<title>&crashprompt.title;</title>
<link rel="stylesheet" href="chrome://browser/skin/platform.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/skin/browser.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/content/browser.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/content/bindings.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/skin/crashprompt.css" type="text/css" media="all" />
<!--TODO: Do we need a favicon?-->

Просмотреть файл

@ -5,9 +5,9 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/bindings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/start.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">

Просмотреть файл

@ -75,7 +75,7 @@ chrome.jar:
content/appbar.js (content/appbar.js)
content/shell.xul (content/jsshell/shell.xul)
content/shell.html (content/jsshell/shell.html)
content/browser.css (content/browser.css)
content/bindings.css (content/bindings.css)
content/cursor.css (content/cursor.css)
content/sanitize.js (content/sanitize.js)
content/sanitizeUI.js (content/sanitizeUI.js)

Просмотреть файл

@ -0,0 +1,111 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gStartView = null;
function test() {
requestLongerTimeout(2);
runTests();
}
function setup() {
PanelUI.hide();
if (!BrowserUI.isStartTabVisible) {
let tab = yield addTab("about:start");
gStartView = tab.browser.contentWindow.BookmarksStartView._view;
yield waitForCondition(() => BrowserUI.isStartTabVisible);
yield hideContextUI();
}
BookmarksTestHelper.setup();
HistoryTestHelper.setup();
}
function tearDown() {
PanelUI.hide();
BookmarksTestHelper.restore();
HistoryTestHelper.restore();
}
gTests.push({
desc: "touch scroll",
run: function run() {
yield addTab(chromeRoot + "res/scroll_test.html");
yield hideContextUI();
yield hideNavBar();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var touchdrag = new TouchDragAndHold();
touchdrag.useNativeEvents = true;
touchdrag.stepTimeout = 5;
touchdrag.numSteps = 20;
stopwatch.start();
let recordingHandle = domUtils.startFrameTimeRecording();
for (let count = 0; count < 3; count++) {
yield touchdrag.start(win, 100, (win.innerHeight - 50), 100, 50);
touchdrag.end();
yield touchdrag.start(win, 100, 50, 100, (win.innerHeight - 50));
touchdrag.end();
}
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareTest("14C693E5-3ED3-4A5D-93BC-A31F130A8CDE",
"touch scroll text", "graphics", "apzc",
"Measures performance of apzc scrolling for a large page of text using FTR.");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
}
});
gTests.push({
desc: "touch scroll start",
setUp: setup,
tearDown: tearDown,
run: function run() {
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var touchdrag = new TouchDragAndHold();
touchdrag.useNativeEvents = true;
touchdrag.stepTimeout = 5;
touchdrag.numSteps = 20;
stopwatch.start();
let recordingHandle = domUtils.startFrameTimeRecording();
for (let count = 0; count < 3; count++) {
yield touchdrag.start(win, (win.innerWidth - 50), (win.innerHeight - 50), 50, (win.innerHeight - 50));
touchdrag.end();
yield touchdrag.start(win, 50, (win.innerHeight - 50), (win.innerWidth - 50), (win.innerHeight - 50));
touchdrag.end();
}
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareTest("24C693E5-3ED3-4A5D-93BC-A31F130A8CDE",
"touch scroll about:start", "graphics", "apzc",
"Measures performance of apzc scrolling for about:start using FTR.");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
let results = PerfTest.computeHighLowBands(intervals, .1);
PerfTest.declareTest("2E60F8B5-8925-4628-988E-E4C0BC6B34C7",
"about:start jank", "graphics", "apzc",
"Displays the low, high, and average FTR frame intervals for about:start.");
info("results.low:" + results.low);
PerfTest.declareNumericalResults([
{ value: results.low, desc: "low" },
{ value: results.high, desc: "high" },
{ value: results.ave, desc: "average" }
]);
}
});

Просмотреть файл

@ -12,19 +12,17 @@ gTests.push({
run: function run() {
yield addTab("about:mozilla");
yield hideContextUI();
yield waitForMs(5000);
yield hideNavBar();
yield waitForMs(3000);
let shiftDataSet = new Array();
let paintDataSet = new Array();
let stopwatch = new StopWatch();
var paintCount = 0;
function onPaint() {
paintCount++;
}
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
for (let idx = 0; idx < 10; idx++) {
window.addEventListener("MozAfterPaint", onPaint, true);
let recordingHandle = domUtils.startFrameTimeRecording();
stopwatch.start();
let promise = waitForEvent(window, "MozDeckOffsetChanged");
ContentAreaObserver.shiftBrowserDeck(300);
@ -34,22 +32,21 @@ gTests.push({
yield promise;
stopwatch.stop();
yield waitForMs(500);
window.removeEventListener("MozAfterPaint", onPaint, true);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
shiftDataSet.push(stopwatch.time());
paintDataSet.push(paintCount);
paintCount = 0;
paintDataSet.push(intervals.length);
}
PerfTest.declareTest("ecb5fbec-0b3d-490f-8d4a-13fa8963e54a",
"shift browser deck", "browser", "ux",
"Triggers multiple SKB deck shifting operations using an offset " +
"value of 300px. Measures total time in milliseconds for a up/down " +
"shift operation plus the total number of paints. Strips outliers.");
"shift operation plus the total number of frames. Strips outliers.");
let shiftAverage = PerfTest.computeAverage(shiftDataSet, { stripOutliers: true });
let paintAverage = PerfTest.computeAverage(paintDataSet, { stripOutliers: true });
PerfTest.declareNumericalResults([
{ value: shiftAverage, desc: "msec" },
{ value: paintAverage, desc: "paint count" },
{ value: paintAverage, desc: "frame count" },
]);
}
});

Просмотреть файл

@ -11,24 +11,24 @@ gTests.push({
desc: "rotating divs",
run: function run() {
yield addTab(chromeRoot + "res/divs_test.html", true);
yield hideContextUI();
yield hideNavBar();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
yield waitForEvent(win, "teststarted", 5000);
// the test runs for five seconds
let recordingHandle = domUtils.startFrameTimeRecording();
stopwatch.start();
let event = yield waitForEvent(win, "testfinished", 10000);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareTest("B924F3FA-4CB5-4453-B131-53E3611E0765",
"rotating divs w/text", "graphics", "content",
"Measures animation frames for rotating translucent divs on top of a background of text.");
stopwatch.start();
// the test runs for ten seconds
let event = yield waitForEvent(win, "testfinished", 20000);
let msec = stopwatch.stop();
PerfTest.declareNumericalResult((event.detail.frames / msec) * 1000.0, "fps");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
}
});

Просмотреть файл

@ -18,81 +18,27 @@ gTests.push({
run: function run() {
yield addTab(chromeRoot + "res/scroll_test.html");
yield hideContextUI();
yield hideNavBar();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
PerfTest.declareTest("79235F74-037C-4F6B-AE47-FDCCC13458C3",
"scrollBy scroll", "graphics", "content",
"Measures performance of single line scrolls using scrollBy for a large page of text.");
let mozpaints = 0;
function onPaint() {
mozpaints++;
}
win.addEventListener("MozAfterPaint", onPaint, true);
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let deferExit = Promise.defer();
let recordingHandle = domUtils.startFrameTimeRecording();
function step() {
if (stopwatch.time() < 10000) {
if (stopwatch.time() < 5000) {
win.scrollBy(0, 2);
// XXX slaves won't paint without this
win.mozRequestAnimationFrame(step);
return;
}
win.removeEventListener("MozAfterPaint", onPaint, true);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareFrameRateResult(mozpaints, msec, "fps");
deferExit.resolve();
}
stopwatch.start();
win.mozRequestAnimationFrame(step);
yield deferExit.promise;
}
});
gTests.push({
desc: "scoll touch",
run: function run() {
yield addTab(chromeRoot + "res/scroll_test.html");
yield hideContextUI();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
PerfTest.declareTest("14C693E5-3ED3-4A5D-93BC-A31F130A8CDE",
"touch scroll", "graphics", "content",
"Measures performance of single line scrolls using touch input for a large page of text.");
let y = win.innerHeight - 10;
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchstart" }, win);
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchmove" }, win);
y -= tapRadius() + 5;
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchmove" }, win);
let mozpaints = 0;
function onPaint() {
mozpaints++;
}
win.addEventListener("MozAfterPaint", onPaint, true);
let deferExit = Promise.defer();
function step() {
if (stopwatch.time() < 10000) {
y -= 2;
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchmove" }, win);
win.mozRequestAnimationFrame(step);
return;
}
win.removeEventListener("MozAfterPaint", onPaint, true);
let msec = stopwatch.stop();
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchend" }, win);
PerfTest.declareFrameRateResult(mozpaints, msec, "fps");
PerfTest.declareTest("79235F74-037C-4F6B-AE47-FDCCC13458C3",
"scrollBy scroll", "graphics", "content",
"Measures performance of single line scrolls for a large page of text using FTR.");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
deferExit.resolve();
}
@ -106,17 +52,22 @@ gTests.push({
desc: "canvas perf test",
run: function run() {
yield addTab(chromeRoot + "res/ripples.html");
let win = Browser.selectedTab.browser.contentWindow;
yield hideContextUI();
yield hideNavBar();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let recordingHandle = domUtils.startFrameTimeRecording();
PerfTest.declareTest("6A455F96-2B2C-4B3C-B387-1AF2F1747CCF",
"ripples", "graphics", "canvas",
"Measures animation frames during a computationally " +
"heavy graphics demo using canvas.");
"heavy graphics demo using canvas using FTR.");
let stopwatch = new StopWatch(true);
let event = yield waitForEvent(win, "test", 20000);
// test page runs for 5 seconds
let event = yield waitForEvent(win, "test", 10000);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareFrameRateResult(event.detail.frames, msec, "fps");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
}
});
@ -125,27 +76,24 @@ gTests.push({
run: function run() {
yield addTab(chromeRoot + "res/tidevideo.html");
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let video = win.document.getElementById("videoelement");
video.pause();
yield hideContextUI();
yield hideNavBar();
yield waitForMs(1000);
PerfTest.declareTest("7F55F9C4-0ECF-4A13-9A9C-A38D46922C0B",
"video playback (moz paints)", "graphics", "video",
"Measures MozAfterPaints per second during five seconds of playback of an mp4.");
"Measures frames per second during five seconds of playback of an mp4.");
var paintCount = 0;
function onPaint() {
paintCount++;
}
let recordingHandle = domUtils.startFrameTimeRecording();
let stopwatch = new StopWatch(true);
window.addEventListener("MozAfterPaint", onPaint, true);
video.play();
yield waitForMs(5000);
video.pause();
window.removeEventListener("MozAfterPaint", onPaint, true);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareNumericalResult((paintCount / msec) * 1000.0, "pps");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
PerfTest.declareTest("E132D333-4642-4597-B1F0-1E74B625DBD7",
"video playback (moz stats)", "graphics", "video",
@ -158,4 +106,3 @@ gTests.push({
PerfTest.declareNumericalResults(results);
}
});

Просмотреть файл

@ -179,6 +179,29 @@ var PerfTest = {
return total / count;
},
computeHighLowBands: function computeHighLow(aArray, aPercentage) {
let bandCount = Math.ceil(aArray.length * aPercentage);
let lowGroup = 0, highGroup = 0;
let idx;
function compareNumbers(a, b) {
return a - b;
}
aArray.sort(compareNumbers);
for (idx = 0; idx < bandCount; idx++) {
lowGroup += aArray[idx];
}
let top = aArray.length - 1;
for (idx = top; idx > (top - bandCount); idx--) {
highGroup += aArray[idx];
}
return {
low: lowGroup / bandCount,
high: highGroup / bandCount,
ave: this.computeAverage(aArray, {})
};
},
/******************************************************
* Internal
*/

Просмотреть файл

@ -11,6 +11,7 @@ support-files =
res/divs_test.html
res/fx.png
[browser_apzc.js]
[browser_miscgfx_01.js]
[browser_tabs_01.js]
[browser_deck_01.js]

Просмотреть файл

@ -70,12 +70,15 @@ var frames = 0;
function go() {
var now = new Date();
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent("teststarted", true, false, {});
window.dispatchEvent(evt);
function step(timestamp) {
render();
frames++;
var time = new Date();
var diff = time.getTime() - now.getTime();
if (diff < 10000) {
if (diff < 5000) {
window.mozRequestAnimationFrame(step);
} else {
var evt = document.createEvent("CustomEvent");

Просмотреть файл

@ -231,7 +231,7 @@ function run() {
ripples.run(progress);
var time = new Date();
var diff = time.getTime() - now.getTime();
if (diff < 10000) { // ten seconds
if (diff < 5000) { // five seconds
window.mozRequestAnimationFrame(step);
} else {
var evt = document.createEvent("CustomEvent");

Просмотреть файл

@ -0,0 +1,27 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* 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";
function test() {
runTests();
}
gTests.push({
desc: "Private tab sanity check",
run: function() {
let tab = Browser.addTab("about:mozilla");
is(tab.isPrivate, false, "Tabs are not private by default");
is(tab.chromeTab.hasAttribute("private"), false,
"non-private tab has no private attribute");
Browser.closeTab(tab, { forceClose: true });
tab = Browser.addTab("about:mozilla", false, null, { private: true });
is(tab.isPrivate, true, "Create a private tab");
is(tab.chromeTab.getAttribute("private"), "true",
"private tab has private attribute");
Browser.closeTab(tab, { forceClose: true });
}
});

Просмотреть файл

@ -6,10 +6,10 @@
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/bindings.css" type="text/css"?>
<!DOCTYPE window []>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<circularprogressindicator id="progress-indicator" oncommand=""/>
</window>
</window>

Просмотреть файл

@ -6,7 +6,7 @@
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/bindings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
<!DOCTYPE window []>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

Просмотреть файл

@ -213,11 +213,22 @@ function hideContextUI()
function showNavBar()
{
let promise = waitForEvent(Elements.navbar, "transitionend");
if (!ContextUI.navbarVisible) {
let promise = waitForEvent(Elements.navbar, "transitionend");
ContextUI.displayNavbar();
return promise;
}
return Promise.resolve(null);
}
function hideNavBar()
{
if (ContextUI.navbarVisible) {
let promise = waitForEvent(Elements.navbar, "transitionend");
ContextUI.dismissNavbar();
return promise;
}
return Promise.resolve(null);
}
function fireAppBarDisplayEvent()
@ -827,6 +838,14 @@ TouchDragAndHold.prototype = {
this._native = aValue;
},
set stepTimeout(aValue) {
this._timeoutStep = aValue;
},
set numSteps(aValue) {
this._numSteps = aValue;
},
set nativePointerId(aValue) {
this._pointerId = aValue;
},

Просмотреть файл

@ -53,6 +53,7 @@ support-files =
[browser_mouse_events.js]
[browser_onscreen_keyboard.js]
[browser_prefs_ui.js]
[browser_private_browsing.js]
[browser_prompt.js]
[browser_remotetabs.js]
[browser_snappedState.js]

Просмотреть файл

@ -553,15 +553,17 @@ SessionStore.prototype = {
},
_getTabData: function(aWindow) {
return aWindow.Browser.tabs.map(tab => {
let browser = tab.browser;
if (browser.__SS_data) {
let tabData = browser.__SS_data;
if (browser.__SS_extdata)
tabData.extData = browser.__SS_extdata;
return tabData;
}
});
return aWindow.Browser.tabs
.filter(tab => !tab.isPrivate)
.map(tab => {
let browser = tab.browser;
if (browser.__SS_data) {
let tabData = browser.__SS_data;
if (browser.__SS_extdata)
tabData.extData = browser.__SS_extdata;
return tabData;
}
});
},
_collectWindowData: function ss__collectWindowData(aWindow) {

Просмотреть файл

@ -9,6 +9,7 @@
<!ENTITY back.label "Back">
<!ENTITY forward.label "Forward">
<!ENTITY newtab.label "New Tab">
<!ENTITY newPrivateTab.label "New Private Tab">
<!ENTITY closetab.label "Close Tab">
<!ENTITY autocompleteResultsHeader.label "Your Results">
@ -103,6 +104,8 @@
<!ENTITY newTab.key "t">
<!ENTITY newTab2.key "n">
<!ENTITY closeTab.key "w">
<!-- Private browsing (control+shift+key) -->
<!ENTITY newPrivateTab.key "p">
<!-- DEVELOPER SHORTCUTS (control+shift+key) -->
<!ENTITY jsConsole.key "j">

Просмотреть файл

@ -33,10 +33,12 @@ pref("metro.debug.selection.displayRanges", false);
pref("metro.debug.selection.dumpRanges", false);
pref("metro.debug.selection.dumpEvents", false);
// Private browsing is disabled by default until implementation and testing are complete
pref("metro.private_browsing.enabled", false);
// Enable tab-modal prompts
pref("prompts.tab_modal.enabled", true);
// Enable off main thread compositing
pref("layers.offmainthreadcomposition.enabled", true);
pref("layers.async-pan-zoom.enabled", true);

Просмотреть файл

@ -163,6 +163,12 @@ documenttab[closing] > .documenttab-container {
background-image: none!important;
}
/* TODO: Decide how and where to display private tabs.
* For now, display them in the main tab strip but hide the thumbnail. */
documenttab[private] .documenttab-thumbnail {
background-color: purple;
}
.documenttab-title {
margin: @metro_spacing_normal@ @metro_spacing_snormal@;
margin-top: 0;
@ -220,43 +226,11 @@ documenttab[selected] .documenttab-selection {
padding-bottom: @toolbar_height@;
}
#startui-page {
overflow-x: scroll;
overflow-y: hidden;
height: 100%;
}
#startui-page[viewstate="snapped"],
#startui-page[viewstate="portrait"] {
overflow-x: hidden;
overflow-y: scroll;
}
#startui-body {
height: 100%;
margin: 0;
}
#start-container {
height: 100%;
}
#urlbar-autocomplete[viewstate="snapped"],
#urlbar-autocomplete[viewstate="portrait"]{
-moz-box-orient: vertical;
}
#start-container[viewstate="snapped"],
#start-container[viewstate="portrait"] {
width: 100%;
max-width: 100%; /* ensure page doesn't expand, messing up @media rules */
height: auto;
}
#start-container[viewstate="snapped"] .meta-section:not([expanded]) > richgrid {
visibility: collapse;
}
#autocomplete-overlay {
display: none;
background-color: black;

Просмотреть файл

@ -23,6 +23,7 @@ chrome.jar:
skin/touchcontrols.css (touchcontrols.css)
* skin/netError.css (netError.css)
skin/firstrun.css (firstrun.css)
* skin/start.css (start.css)
% override chrome://global/skin/about.css chrome://browser/skin/about.css
% override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
% override chrome://global/skin/netError.css chrome://browser/skin/netError.css

Просмотреть файл

@ -0,0 +1,40 @@
/* 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/. */
%filter substitution
%include defines.inc
/* Start UI ----------------------------------------------------------------- */
#startui-page {
overflow-x: scroll;
overflow-y: hidden;
height: 100%;
}
#startui-page[viewstate="snapped"],
#startui-page[viewstate="portrait"] {
overflow-x: hidden;
overflow-y: scroll;
}
#startui-body {
height: 100%;
margin: 0;
}
#start-container {
height: 100%;
}
#start-container[viewstate="snapped"],
#start-container[viewstate="portrait"] {
width: 100%;
max-width: 100%; /* ensure page doesn't expand, messing up @media rules */
height: auto;
}
#start-container[viewstate="snapped"] .meta-section:not([expanded]) > richgrid {
visibility: collapse;
}

Просмотреть файл

@ -50,6 +50,10 @@
#customization-panel-container {
padding: 15px 25px 25px;
background-image: linear-gradient(to bottom, #3e86ce, #3878ba);
}
#main-window[customize-entered] #customization-panel-container {
background-image: url("chrome://browser/skin/customizableui/customizeMode-separatorHorizontal.png"),
url("chrome://browser/skin/customizableui/customizeMode-separatorVertical.png"),
url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
@ -75,6 +79,7 @@ toolbarpaletteitem[place="panel"] {
transition: transform .3s ease-in-out;
}
toolbarpaletteitem[notransition].panel-customization-placeholder,
toolbarpaletteitem[notransition][place="palette"],
toolbarpaletteitem[notransition][place="panel"] {
transition: none;

Просмотреть файл

@ -6,8 +6,8 @@
%define menuPanelWidth 22.35em
%define exitSubviewGutterWidth 38px
%define buttonStateHover :not(:-moz-any([disabled],[open],[checked="true"],[_moz-menuactive="true"],:active)):hover
%define buttonStateActive :not([disabled]):-moz-any([open],[checked="true"],[_moz-menuactive="true"],:hover:active)
%define buttonStateHover :not(:-moz-any([disabled],[checked="true"],[open],:active)):hover
%define buttonStateActive :not([disabled]):-moz-any([open],[checked="true"],:hover:active)
%include ../browser.inc
@ -510,7 +510,9 @@ toolbarpaletteitem[place="palette"] > #search-container {
of 3px. */
min-width: calc(@menuPanelWidth@ / 3 - 2px);
max-width: calc(@menuPanelWidth@ / 3 - 2px);
height: auto;
/* We'd prefer to use height: auto here but it leads to layout bugs in the panel. Cope:
1.2em for line height + 2 * .5em padding + margin on the label (2 * 2px) */
height: calc(2.2em + 4px);
max-height: none;
-moz-box-orient: horizontal;
}

Просмотреть файл

@ -294,7 +294,7 @@ case "$target" in
# The build tools got moved around to different directories in
# SDK Tools r22. Try to locate them.
android_build_tools=""
for suffix in android-4.3 19.0.0 18.1.0 18.0.1 18.0.0 17.0.0 android-4.2.2; do
for suffix in android-4.4 android-4.3 android-4.2.2 19.0.0 18.1.0 18.0.1 18.0.0 17.0.0; do
tools_directory="$android_sdk_root/build-tools/$suffix"
if test -d "$tools_directory" ; then
android_build_tools="$tools_directory"

Просмотреть файл

@ -300,7 +300,7 @@
android:authorities="@ANDROID_PACKAGE_NAME@.db.tabs"
android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
<provider android:name="org.mozilla.gecko.db.HomeListsProvider"
<provider android:name="org.mozilla.gecko.db.HomeProvider"
android:authorities="@ANDROID_PACKAGE_NAME@.db.homelists"
android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>

Просмотреть файл

@ -24,8 +24,8 @@ public class BrowserContract {
public static final String TABS_AUTHORITY = AppConstants.ANDROID_PACKAGE_NAME + ".db.tabs";
public static final Uri TABS_AUTHORITY_URI = Uri.parse("content://" + TABS_AUTHORITY);
public static final String HOME_LISTS_AUTHORITY = AppConstants.ANDROID_PACKAGE_NAME + ".db.homelists";
public static final Uri HOME_LISTS_AUTHORITY_URI = Uri.parse("content://" + HOME_LISTS_AUTHORITY);
public static final String HOME_AUTHORITY = AppConstants.ANDROID_PACKAGE_NAME + ".db.home";
public static final Uri HOME_AUTHORITY_URI = Uri.parse("content://" + HOME_AUTHORITY);
public static final String PARAM_PROFILE = "profile";
public static final String PARAM_PROFILE_PATH = "profilePath";
@ -288,16 +288,21 @@ public class BrowserContract {
public static final String LAST_MODIFIED = "last_modified";
}
// Data storage for custom lists on about:home
// Data storage for dynamic panels on about:home
@RobocopTarget
public static final class HomeListItems implements CommonColumns, URLColumns {
private HomeListItems() {}
public static final Uri CONTENT_FAKE_URI = Uri.withAppendedPath(HOME_LISTS_AUTHORITY_URI, "items/fake");
public static final Uri CONTENT_URI = Uri.withAppendedPath(HOME_LISTS_AUTHORITY_URI, "items");
public static final class HomeItems implements CommonColumns {
private HomeItems() {}
public static final Uri CONTENT_FAKE_URI = Uri.withAppendedPath(HOME_AUTHORITY_URI, "items/fake");
public static final Uri CONTENT_URI = Uri.withAppendedPath(HOME_AUTHORITY_URI, "items");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/homelistitem";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/homelistitem";
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/homeitem";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/homeitem";
public static final String PROVIDER_ID = "provider_id";
public static final String DATASET_ID = "dataset_id";
public static final String URL = "url";
public static final String TITLE = "title";
public static final String DESCRIPTION = "description";
public static final String IMAGE_URL = "image_url";
public static final String CREATED = "created";
}
}

Просмотреть файл

@ -5,7 +5,7 @@
package org.mozilla.gecko.db;
import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract.HomeListItems;
import org.mozilla.gecko.db.BrowserContract.HomeItems;
import org.mozilla.gecko.db.PerProfileDatabases.DatabaseHelperFactory;
import org.mozilla.gecko.sqlite.SQLiteBridge;
@ -27,8 +27,8 @@ import android.util.Log;
import java.io.InputStream;
import java.io.IOException;
public class HomeListsProvider extends SQLiteBridgeContentProvider {
private static final String LOGTAG = "GeckoHomeListsProvider";
public class HomeProvider extends SQLiteBridgeContentProvider {
private static final String LOGTAG = "GeckoHomeProvider";
// This should be kept in sync with the db version in mobile/android/modules/HomeProvider.jsm
private static int DB_VERSION = 1;
@ -44,12 +44,12 @@ public class HomeListsProvider extends SQLiteBridgeContentProvider {
static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static {
URI_MATCHER.addURI(BrowserContract.HOME_LISTS_AUTHORITY, "items/fake", ITEMS_FAKE);
URI_MATCHER.addURI(BrowserContract.HOME_LISTS_AUTHORITY, "items", ITEMS);
URI_MATCHER.addURI(BrowserContract.HOME_LISTS_AUTHORITY, "items/#", ITEMS_ID);
URI_MATCHER.addURI(BrowserContract.HOME_AUTHORITY, "items/fake", ITEMS_FAKE);
URI_MATCHER.addURI(BrowserContract.HOME_AUTHORITY, "items", ITEMS);
URI_MATCHER.addURI(BrowserContract.HOME_AUTHORITY, "items/#", ITEMS_ID);
}
public HomeListsProvider() {
public HomeProvider() {
super(LOGTAG);
}
@ -59,10 +59,10 @@ public class HomeListsProvider extends SQLiteBridgeContentProvider {
switch (match) {
case ITEMS_FAKE: {
return HomeListItems.CONTENT_TYPE;
return HomeItems.CONTENT_TYPE;
}
case ITEMS: {
return HomeListItems.CONTENT_TYPE;
return HomeItems.CONTENT_TYPE;
}
default: {
throw new UnsupportedOperationException("Unknown type " + uri);
@ -91,18 +91,19 @@ public class HomeListsProvider extends SQLiteBridgeContentProvider {
try {
items = new JSONArray(getRawFakeItems());
} catch (IOException e) {
Log.e(LOGTAG, "Error getting fake list items", e);
Log.e(LOGTAG, "Error getting fake home items", e);
return null;
} catch (JSONException e) {
Log.e(LOGTAG, "Error parsing fake-list-items.json", e);
Log.e(LOGTAG, "Error parsing fake_home_items.json", e);
return null;
}
final String[] itemsColumns = new String[] {
HomeListItems._ID,
HomeListItems.PROVIDER_ID,
HomeListItems.URL,
HomeListItems.TITLE
HomeItems._ID,
HomeItems.DATASET_ID,
HomeItems.URL,
HomeItems.TITLE,
HomeItems.DESCRIPTION
};
final MatrixCursor c = new MatrixCursor(itemsColumns);
@ -111,19 +112,20 @@ public class HomeListsProvider extends SQLiteBridgeContentProvider {
final JSONObject item = items.getJSONObject(i);
c.addRow(new Object[] {
item.getInt("id"),
item.getString("provider_id"),
item.getString("dataset_id"),
item.getString("url"),
item.getString("title")
item.getString("title"),
item.getString("description")
});
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating cursor row for fake list item", e);
Log.e(LOGTAG, "Error creating cursor row for fake home item", e);
}
}
return c;
}
private String getRawFakeItems() throws IOException {
final InputStream inputStream = getContext().getResources().openRawResource(R.raw.fake_list_items);
final InputStream inputStream = getContext().getResources().openRawResource(R.raw.fake_home_items);
final byte[] buffer = new byte[1024];
StringBuilder s = new StringBuilder();
int count;

Просмотреть файл

@ -7,9 +7,10 @@ package org.mozilla.gecko.home;
import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.HomeListItems;
import org.mozilla.gecko.db.BrowserContract.HomeItems;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.PanelLayout.DatasetHandler;
import android.app.Activity;
import android.content.ContentResolver;
@ -18,6 +19,7 @@ import android.content.res.Configuration;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
@ -30,24 +32,38 @@ import android.widget.ListView;
import java.util.EnumSet;
/**
* Fragment that displays dynamic content specified by a PanelConfig.
* Fragment that displays dynamic content specified by a {@code PanelConfig}.
* The {@code DynamicPanel} UI is built based on the given {@code LayoutType}
* and its associated list of {@code ViewConfig}.
*
* {@code DynamicPanel} manages all necessary Loaders to load panel datasets
* from their respective content providers. Each panel dataset has its own
* associated Loader. This is enforced by defining the Loader IDs based on
* their associated dataset IDs.
*
* The {@code PanelLayout} can make load and reset requests on datasets via
* the provided {@code DatasetHandler}. This way it doesn't need to know the
* details of how datasets are loaded and reset. Each time a dataset is
* requested, {@code DynamicPanel} restarts a Loader with the respective ID (see
* {@code PanelDatasetHandler}).
*
* See {@code PanelLayout} for more details on how {@code DynamicPanel}
* receives dataset requests and delivers them back to the {@code PanelLayout}.
*/
public class DynamicPanel extends HomeFragment {
private static final String LOGTAG = "GeckoDynamicPanel";
// Cursor loader ID for the lists
private static final int LOADER_ID_LIST = 0;
// Dataset ID to be used by the loader
private static final String DATASET_ID = "dataset_id";
// The panel layout associated with this panel
private PanelLayout mLayout;
// The configuration associated with this panel
private PanelConfig mPanelConfig;
// XXX: Right now DynamicPanel is hard-coded to show a single ListView,
// but it should dynamically build views from mPanelConfig (bug 959777).
private HomeListAdapter mAdapter;
private ListView mList;
// Callbacks used for the list loader
private CursorLoaderCallbacks mCursorLoaderCallbacks;
// Callbacks used for the loader
private PanelLoaderCallbacks mLoaderCallbacks;
// On URL open listener
private OnUrlOpenListener mUrlOpenListener;
@ -87,22 +103,30 @@ public class DynamicPanel extends HomeFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// XXX: Dynamically build views from mPanelConfig (bug 959777).
mList = new HomeListView(getActivity());
return mList;
switch(mPanelConfig.getLayoutType()) {
case FRAME:
final PanelDatasetHandler datasetHandler = new PanelDatasetHandler();
mLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler);
break;
default:
throw new IllegalStateException("Unrecognized layout type in DynamicPanel");
}
Log.d(LOGTAG, "Created layout of type: " + mPanelConfig.getLayoutType());
return mLayout;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
registerForContextMenu(mList);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mList = null;
mLayout = null;
}
@Override
@ -122,29 +146,73 @@ public class DynamicPanel extends HomeFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// XXX: Dynamically set adapters from mPanelConfig (bug 959777).
mAdapter = new HomeListAdapter(getActivity(), null);
mList.setAdapter(mAdapter);
// Create callbacks before the initial loader is started.
mCursorLoaderCallbacks = new CursorLoaderCallbacks();
mLoaderCallbacks = new PanelLoaderCallbacks();
loadIfVisible();
}
@Override
protected void load() {
getLoaderManager().initLoader(LOADER_ID_LIST, null, mCursorLoaderCallbacks);
Log.d(LOGTAG, "Loading layout");
mLayout.load();
}
private static int generateLoaderId(String datasetId) {
return datasetId.hashCode();
}
/**
* Cursor loader for the lists.
* Used by the PanelLayout to make load and reset requests to
* the holding fragment.
*/
private static class HomeListLoader extends SimpleCursorLoader {
private String mProviderId;
private class PanelDatasetHandler implements DatasetHandler {
@Override
public void requestDataset(String datasetId) {
Log.d(LOGTAG, "Requesting dataset: " + datasetId);
public HomeListLoader(Context context, String providerId) {
// Ignore dataset requests while the fragment is not
// allowed to load its content.
if (!getCanLoadHint()) {
return;
}
final Bundle bundle = new Bundle();
bundle.putString(DATASET_ID, datasetId);
// Ensure one loader per dataset
final int loaderId = generateLoaderId(datasetId);
getLoaderManager().restartLoader(loaderId, bundle, mLoaderCallbacks);
}
@Override
public void resetDataset(String datasetId) {
Log.d(LOGTAG, "Resetting dataset: " + datasetId);
final LoaderManager lm = getLoaderManager();
final int loaderId = generateLoaderId(datasetId);
// Release any resources associated with the dataset if
// it's currently loaded in memory.
final Loader<?> datasetLoader = lm.getLoader(loaderId);
if (datasetLoader != null) {
datasetLoader.reset();
}
}
}
/**
* Cursor loader for the panel datasets.
*/
private static class PanelDatasetLoader extends SimpleCursorLoader {
private final String mDatasetId;
public PanelDatasetLoader(Context context, String datasetId) {
super(context);
mProviderId = providerId;
mDatasetId = datasetId;
}
public String getDatasetId() {
return mDatasetId;
}
@Override
@ -152,55 +220,45 @@ public class DynamicPanel extends HomeFragment {
final ContentResolver cr = getContext().getContentResolver();
// XXX: Use the test URI for static fake data
final Uri fakeItemsUri = HomeListItems.CONTENT_FAKE_URI.buildUpon().
final Uri fakeItemsUri = HomeItems.CONTENT_FAKE_URI.buildUpon().
appendQueryParameter(BrowserContract.PARAM_PROFILE, "default").build();
final String selection = HomeListItems.PROVIDER_ID + " = ?";
final String[] selectionArgs = new String[] { mProviderId };
final String selection = HomeItems.DATASET_ID + " = ?";
final String[] selectionArgs = new String[] { mDatasetId };
Log.i(LOGTAG, "Loading fake data for list provider: " + mProviderId);
Log.i(LOGTAG, "Loading fake data for list provider: " + mDatasetId);
return cr.query(fakeItemsUri, null, selection, selectionArgs, null);
}
}
/**
* Cursor adapter for the list.
*/
private class HomeListAdapter extends CursorAdapter {
public HomeListAdapter(Context context, Cursor cursor) {
super(context, cursor, 0);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final TwoLinePageRow row = (TwoLinePageRow) view;
row.updateFromCursor(cursor);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item_row, parent, false);
}
}
/**
* LoaderCallbacks implementation that interacts with the LoaderManager.
*/
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
private class PanelLoaderCallbacks implements LoaderCallbacks<Cursor> {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new HomeListLoader(getActivity(), mPanelConfig.getId());
final String datasetId = args.getString(DATASET_ID);
Log.d(LOGTAG, "Creating loader for dataset: " + datasetId);
return new PanelDatasetLoader(getActivity(), datasetId);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
mAdapter.swapCursor(c);
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
final PanelDatasetLoader datasetLoader = (PanelDatasetLoader) loader;
Log.d(LOGTAG, "Finished loader for dataset: " + datasetLoader.getDatasetId());
mLayout.deliverDataset(datasetLoader.getDatasetId(), cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
final PanelDatasetLoader datasetLoader = (PanelDatasetLoader) loader;
Log.d(LOGTAG, "Resetting loader for dataset: " + datasetLoader.getDatasetId());
if (mLayout != null) {
mLayout.releaseDataset(datasetLoader.getDatasetId());
}
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.home;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import android.content.Context;
import android.util.Log;
import android.view.View;
class FramePanelLayout extends PanelLayout {
private static final String LOGTAG = "GeckoFramePanelLayout";
private final View mChildView;
private final ViewConfig mChildConfig;
public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler) {
super(context, panelConfig, datasetHandler);
// This layout can only hold one view so we simply
// take the first defined view from PanelConfig.
mChildConfig = panelConfig.getViewAt(0);
if (mChildConfig == null) {
throw new IllegalStateException("FramePanelLayout requires a view in PanelConfig");
}
mChildView = createPanelView(mChildConfig);
addView(mChildView);
}
@Override
public void load() {
Log.d(LOGTAG, "Loading");
if (mChildView instanceof DatasetBacked) {
Log.d(LOGTAG, "Requesting child dataset: " + mChildConfig.getDatasetId());
requestDataset(mChildConfig.getDatasetId());
}
}
}

Просмотреть файл

@ -0,0 +1,227 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.home;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
/**
* {@code PanelLayout} is the base class for custom layouts to be
* used in {@code DynamicPanel}. It provides the basic framework
* that enables custom layouts to request and reset datasets and
* create panel views. Furthermore, it automates most of the process
* of binding panel views with their respective datasets.
*
* {@code PanelLayout} abstracts the implemention details of how
* datasets are actually loaded through the {@DatasetHandler} interface.
* {@code DatasetHandler} provides two operations: request and reset.
* The results of the dataset requests done via the {@code DatasetHandler}
* are delivered to the {@code PanelLayout} with the {@code deliverDataset()}
* method.
*
* Subclasses of {@code PanelLayout} should simply use the utilities
* provided by {@code PanelLayout}. Namely:
*
* {@code requestDataset()} - To fetch datasets and auto-bind them to
* the existing panel views backed by them.
*
* {@code resetDataset()} - To release any resources associated with a
* previously loaded dataset.
*
* {@code createPanelView()} - To create a panel view for a ViewConfig
* associated with the panel.
*
* {@code disposePanelView()} - To dispose any dataset references associated
* with the given view.
*
* {@code PanelLayout} subclasses should always use {@code createPanelView()}
* to create the views dynamically created based on {@code ViewConfig}. This
* allows {@code PanelLayout} to auto-bind datasets with panel views.
* {@code PanelLayout} subclasses are free to have any type of views to arrange
* the panel views in different ways.
*/
abstract class PanelLayout extends FrameLayout {
private static final String LOGTAG = "GeckoPanelLayout";
private final List<ViewEntry> mViewEntries;
private final DatasetHandler mDatasetHandler;
/**
* To be used by panel views to express that they are
* backed by datasets.
*/
public interface DatasetBacked {
public void setDataset(Cursor cursor);
}
/**
* Defines the contract with the component that is responsible
* for handling datasets requests.
*/
public interface DatasetHandler {
/**
* Requests a dataset to be fetched and auto-bound to the
* panel views backed by it.
*/
public void requestDataset(String datasetId);
/**
* Releases any resources associated with a previously loaded
* dataset. It will do nothing if the dataset with the given ID
* hasn't been loaded before.
*/
public void resetDataset(String datasetId);
}
public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler) {
super(context);
mViewEntries = new ArrayList<ViewEntry>();
mDatasetHandler = datasetHandler;
}
/**
* Delivers the dataset as a {@code Cursor} to be bound to the
* panel views backed by it. This is used by the {@code DatasetHandler}
* in response to a dataset request.
*/
public final void deliverDataset(String datasetId, Cursor cursor) {
Log.d(LOGTAG, "Delivering dataset: " + datasetId);
updateViewsWithDataset(datasetId, cursor);
}
/**
* Releases any references to the given dataset from all
* existing panel views.
*/
public final void releaseDataset(String datasetId) {
Log.d(LOGTAG, "Resetting dataset: " + datasetId);
updateViewsWithDataset(datasetId, null);
}
/**
* Requests a dataset to be loaded and bound to any existing
* panel view backed by it.
*/
protected final void requestDataset(String datasetId) {
Log.d(LOGTAG, "Requesting dataset: " + datasetId);
mDatasetHandler.requestDataset(datasetId);
}
/**
* Releases any resources associated with a previously
* loaded dataset e.g. close any associated {@code Cursor}.
*/
protected final void resetDataset(String datasetId) {
mDatasetHandler.resetDataset(datasetId);
}
/**
* Factory method to create instance of panels from a given
* {@code ViewConfig}. All panel views defined in {@code PanelConfig}
* should be created using this method so that {@PanelLayout} can
* keep track of panel views and their associated datasets.
*/
protected final View createPanelView(ViewConfig viewConfig) {
final View view;
Log.d(LOGTAG, "Creating panel view: " + viewConfig.getType());
switch(viewConfig.getType()) {
case LIST:
view = new PanelListView(getContext(), viewConfig);
break;
default:
throw new IllegalStateException("Unrecognized view type in " + getClass().getSimpleName());
}
final ViewEntry entry = new ViewEntry(view, viewConfig);
mViewEntries.add(entry);
return view;
}
/**
* Dispose any dataset references associated with the
* given view.
*/
protected final void disposePanelView(View view) {
Log.d(LOGTAG, "Disposing panel view");
final int count = mViewEntries.size();
for (int i = 0; i < count; i++) {
final View entryView = mViewEntries.get(i).getView();
if (view == entryView) {
// Release any Cursor references from the view
// if it's backed by a dataset.
maybeSetDataset(entryView, null);
// Remove the view entry from the list
mViewEntries.remove(i);
break;
}
}
}
private void updateViewsWithDataset(String datasetId, Cursor cursor) {
final int count = mViewEntries.size();
for (int i = 0; i < count; i++) {
final ViewEntry entry = mViewEntries.get(i);
// Update any views associated with the given dataset ID
if (TextUtils.equals(entry.getDatasetId(), datasetId)) {
final View view = entry.getView();
maybeSetDataset(view, cursor);
}
}
}
private void maybeSetDataset(View view, Cursor cursor) {
if (view instanceof DatasetBacked) {
final DatasetBacked dsb = (DatasetBacked) view;
dsb.setDataset(cursor);
}
}
/**
* Must be implemented by {@code PanelLayout} subclasses to define
* what happens then the layout is first loaded. Should set initial
* UI state and request any necessary datasets.
*/
public abstract void load();
/**
* Represents a 'live' instance of a panel view associated with
* the {@code PanelLayout}.
*/
private static class ViewEntry {
private final View mView;
private final ViewConfig mViewConfig;
public ViewEntry(View view, ViewConfig viewConfig) {
mView = view;
mViewConfig = viewConfig;
}
public View getView() {
return mView;
}
public String getDatasetId() {
return mViewConfig.getDatasetId();
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.home;
import android.util.Log;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserDB.URLColumns;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.FaviconView;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import java.lang.ref.WeakReference;
public class PanelListRow extends TwoLineRow {
public PanelListRow(Context context) {
this(context, null);
}
public PanelListRow(Context context, AttributeSet attrs) {
super(context, attrs);
// XXX: Never show icon for now. We have to figure out
// how the images will be passed through the cursor.
final View iconView = findViewById(R.id.icon);
iconView.setVisibility(View.GONE);
}
@Override
public void updateFromCursor(Cursor cursor) {
if (cursor == null) {
return;
}
// XXX: This will have to be updated once we come up with the
// final schema for Panel datasets (see bug 942288).
int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
final String title = cursor.getString(titleIndex);
setPrimaryText(title);
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
final String url = cursor.getString(urlIndex);
setSecondaryText(url);
}
}

Просмотреть файл

@ -0,0 +1,57 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.home;
import org.mozilla.gecko.R;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.PanelLayout.DatasetBacked;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class PanelListView extends HomeListView
implements DatasetBacked {
private static final String LOGTAG = "GeckoPanelListView";
private final PanelListAdapter mAdapter;
private final ViewConfig mViewConfig;
public PanelListView(Context context, ViewConfig viewConfig) {
super(context);
mViewConfig = viewConfig;
mAdapter = new PanelListAdapter(context);
setAdapter(mAdapter);
}
@Override
public void setDataset(Cursor cursor) {
Log.d(LOGTAG, "Setting dataset: " + mViewConfig.getDatasetId());
mAdapter.swapCursor(cursor);
}
private class PanelListAdapter extends CursorAdapter {
public PanelListAdapter(Context context) {
super(context, null, 0);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final PanelListRow row = (PanelListRow) view;
row.updateFromCursor(cursor);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.panel_list_row, parent, false);
}
}
}

Просмотреть файл

@ -21,23 +21,14 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class TwoLinePageRow extends LinearLayout
public class TwoLinePageRow extends TwoLineRow
implements Tabs.OnTabsChangedListener {
private static final int NO_ICON = 0;
private final TextView mTitle;
private final TextView mUrl;
private final FaviconView mFavicon;
private int mUrlIconId;
private int mBookmarkIconId;
private boolean mShowIcons;
private int mLoadFaviconJobId = Favicons.NOT_LOADING;
@ -79,16 +70,9 @@ public class TwoLinePageRow extends LinearLayout
public TwoLinePageRow(Context context, AttributeSet attrs) {
super(context, attrs);
setGravity(Gravity.CENTER_VERTICAL);
mUrlIconId = NO_ICON;
mBookmarkIconId = NO_ICON;
mShowIcons = true;
LayoutInflater.from(context).inflate(R.layout.two_line_page_row, this);
mTitle = (TextView) findViewById(R.id.title);
mUrl = (TextView) findViewById(R.id.url);
mFavicon = (FaviconView) findViewById(R.id.favicon);
mFavicon = (FaviconView) findViewById(R.id.icon);
mFaviconListener = new UpdateViewFaviconLoadedListener(mFavicon);
}
@ -120,36 +104,6 @@ public class TwoLinePageRow extends LinearLayout
}
}
private void setTitle(String title) {
mTitle.setText(title);
}
private void setUrl(String url) {
mUrl.setText(url);
}
private void setUrl(int stringId) {
mUrl.setText(stringId);
}
private void setUrlIcon(int urlIconId) {
if (mUrlIconId == urlIconId) {
return;
}
mUrlIconId = urlIconId;
mUrl.setCompoundDrawablesWithIntrinsicBounds(mUrlIconId, 0, mBookmarkIconId, 0);
}
private void setBookmarkIcon(int bookmarkIconId) {
if (mBookmarkIconId == bookmarkIconId) {
return;
}
mBookmarkIconId = bookmarkIconId;
mUrl.setCompoundDrawablesWithIntrinsicBounds(mUrlIconId, 0, mBookmarkIconId, 0);
}
/**
* Stores the page URL, so that we can use it to replace "Switch to tab" if the open
* tab changes or is closed.
@ -168,11 +122,11 @@ public class TwoLinePageRow extends LinearLayout
boolean isPrivate = Tabs.getInstance().getSelectedTab().isPrivate();
int tabId = Tabs.getInstance().getTabIdForUrl(mPageUrl, isPrivate);
if (!mShowIcons || tabId < 0) {
setUrl(mPageUrl);
setUrlIcon(NO_ICON);
setSecondaryText(mPageUrl);
setSecondaryIcon(NO_ICON);
} else {
setUrl(R.string.switch_to_tab);
setUrlIcon(R.drawable.ic_url_bar_tab);
setSecondaryText(R.string.switch_to_tab);
setSecondaryIcon(R.drawable.ic_url_bar_tab);
}
}
@ -180,6 +134,7 @@ public class TwoLinePageRow extends LinearLayout
mShowIcons = showIcons;
}
@Override
public void updateFromCursor(Cursor cursor) {
if (cursor == null) {
return;
@ -207,20 +162,20 @@ public class TwoLinePageRow extends LinearLayout
// The bookmark id will be 0 (null in database) when the url
// is not a bookmark.
if (bookmarkId == 0) {
setBookmarkIcon(NO_ICON);
setPrimaryIcon(NO_ICON);
} else if (display == Combined.DISPLAY_READER) {
setBookmarkIcon(R.drawable.ic_url_bar_reader);
setPrimaryIcon(R.drawable.ic_url_bar_reader);
} else {
setBookmarkIcon(R.drawable.ic_url_bar_star);
setPrimaryIcon(R.drawable.ic_url_bar_star);
}
} else {
setBookmarkIcon(NO_ICON);
setPrimaryIcon(NO_ICON);
}
}
// Use the URL instead of an empty title for consistency with the normal URL
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
setTitle(TextUtils.isEmpty(title) ? url : title);
setPrimaryText(TextUtils.isEmpty(title) ? url : title);
// No point updating the below things if URL has not changed. Prevents evil Favicon flicker.
if (url.equals(mPageUrl)) {

Просмотреть файл

@ -0,0 +1,77 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.home;
import org.mozilla.gecko.R;
import android.content.Context;
import android.database.Cursor;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public abstract class TwoLineRow extends LinearLayout {
protected static final int NO_ICON = 0;
private final TextView mPrimaryText;
private int mPrimaryIconId;
private final TextView mSecondaryText;
private int mSecondaryIconId;
public TwoLineRow(Context context) {
this(context, null);
}
public TwoLineRow(Context context, AttributeSet attrs) {
super(context, attrs);
setGravity(Gravity.CENTER_VERTICAL);
mSecondaryIconId = NO_ICON;
mPrimaryIconId = NO_ICON;
LayoutInflater.from(context).inflate(R.layout.two_line_row, this);
mPrimaryText = (TextView) findViewById(R.id.primary_text);
mSecondaryText = (TextView) findViewById(R.id.secondary_text);
}
protected void setPrimaryText(String text) {
mPrimaryText.setText(text);
}
protected void setSecondaryText(String text) {
mSecondaryText.setText(text);
}
protected void setSecondaryText(int stringId) {
mSecondaryText.setText(stringId);
}
protected void setPrimaryIcon(int iconId) {
if (mPrimaryIconId == iconId) {
return;
}
mPrimaryIconId = iconId;
mSecondaryText.setCompoundDrawablesWithIntrinsicBounds(mSecondaryIconId, 0, mPrimaryIconId, 0);
}
protected void setSecondaryIcon(int iconId) {
if (mSecondaryIconId == iconId) {
return;
}
mSecondaryIconId = iconId;
mSecondaryText.setCompoundDrawablesWithIntrinsicBounds(mSecondaryIconId, 0, mPrimaryIconId, 0);
}
public abstract void updateFromCursor(Cursor cursor);
}

Просмотреть файл

@ -115,7 +115,7 @@ gbjar.sources += [
'db/BrowserProvider.java',
'db/DBUtils.java',
'db/FormHistoryProvider.java',
'db/HomeListsProvider.java',
'db/HomeProvider.java',
'db/LocalBrowserDB.java',
'db/PasswordsProvider.java',
'db/PerProfileDatabases.java',
@ -215,6 +215,7 @@ gbjar.sources += [
'home/BrowserSearch.java',
'home/DynamicPanel.java',
'home/FadedTextView.java',
'home/FramePanelLayout.java',
'home/HistoryPanel.java',
'home/HomeAdapter.java',
'home/HomeBanner.java',
@ -228,6 +229,9 @@ gbjar.sources += [
'home/LastTabsPanel.java',
'home/MostRecentPanel.java',
'home/MultiTypeCursorAdapter.java',
'home/PanelLayout.java',
'home/PanelListRow.java',
'home/PanelListView.java',
'home/PanelManager.java',
'home/PinSiteDialog.java',
'home/ReadingListPanel.java',
@ -242,6 +246,7 @@ gbjar.sources += [
'home/TopSitesPanel.java',
'home/TopSitesThumbnailView.java',
'home/TwoLinePageRow.java',
'home/TwoLineRow.java',
'InputMethods.java',
'JavaAddonManager.java',
'LightweightTheme.java',

Просмотреть файл

@ -20,17 +20,13 @@ public class PanelsPreference extends CustomListPreference {
private static final int INDEX_SHOW_BUTTON = 1;
private static final int INDEX_REMOVE_BUTTON = 2;
private final String LABEL_HIDE;
private final String LABEL_SHOW;
private String LABEL_HIDE;
private String LABEL_SHOW;
protected boolean mIsHidden = false;
public PanelsPreference(Context context, CustomListCategory parentCategory) {
super(context, parentCategory);
Resources res = getContext().getResources();
LABEL_HIDE = res.getString(R.string.pref_panels_hide);
LABEL_SHOW = res.getString(R.string.pref_panels_show);
}
@Override
@ -56,6 +52,9 @@ public class PanelsPreference extends CustomListPreference {
@Override
protected String[] getDialogStrings() {
Resources res = getContext().getResources();
LABEL_HIDE = res.getString(R.string.pref_panels_hide);
LABEL_SHOW = res.getString(R.string.pref_panels_show);
// XXX: Don't provide the "Remove" string for now, because we only support built-in
// panels, which can only be disabled.
return new String[] { LABEL_SET_AS_DEFAULT,

Просмотреть файл

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<org.mozilla.gecko.home.PanelListRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="@dimen/page_row_height"
android:paddingLeft="10dip"
android:minHeight="@dimen/page_row_height"/>

Просмотреть файл

@ -6,7 +6,7 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<org.mozilla.gecko.widget.FaviconView android:id="@+id/favicon"
<org.mozilla.gecko.widget.FaviconView android:id="@+id/icon"
android:layout_width="@dimen/favicon_bg"
android:layout_height="@dimen/favicon_bg"
android:layout_marginLeft="10dip"
@ -18,14 +18,14 @@
android:orientation="vertical">
<org.mozilla.gecko.home.FadedTextView
android:id="@+id/title"
style="@style/Widget.TwoLinePageRow.Title"
android:id="@+id/primary_text"
style="@style/Widget.TwoLineRow.PrimaryText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
gecko:fadeWidth="30dp"/>
<TextView android:id="@+id/url"
style="@style/Widget.TwoLinePageRow.Url"
<TextView android:id="@+id/secondary_text"
style="@style/Widget.TwoLineRow.SecondaryText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawablePadding="5dp"

Просмотреть файл

@ -0,0 +1,13 @@
[{
"id": 1,
"dataset_id": "fake-dataset",
"url": "http://example.com/first",
"title": "First Example",
"description": "This is an example"
}, {
"id": 2,
"dataset_id": "fake-dataset",
"url": "http://example.com/second",
"title": "Second Example",
"description": "This is a second example"
}]

Просмотреть файл

@ -1,11 +0,0 @@
[{
"id": 1,
"provider_id": "fake-provider",
"url": "http://example.com",
"title": "Example"
}, {
"id": 2,
"provider_id": "fake-provider",
"url": "http://mozilla.org",
"title": "Mozilla"
}]

Просмотреть файл

@ -9,13 +9,13 @@
<item name="android:orientation">vertical</item>
</style>
<style name="Widget.BookmarkFolderView" parent="Widget.TwoLinePageRow.Title">
<style name="Widget.BookmarkFolderView" parent="Widget.TwoLineRow.PrimaryText">
<item name="android:paddingLeft">60dip</item>
<item name="android:drawablePadding">10dip</item>
<item name="android:drawableLeft">@drawable/bookmark_folder</item>
</style>
<style name="Widget.BookmarkItemView" parent="Widget.TwoLinePageRow">
<style name="Widget.BookmarkItemView" parent="Widget.TwoLineRow">
<item name="android:paddingLeft">50dp</item>
<item name="android:paddingRight">50dp</item>
</style>

Просмотреть файл

@ -9,7 +9,7 @@
<item name="android:fontFamily">sans-serif-light</item>
</style>
<style name="TextAppearance.Widget.TwoLinePageRow.Title" parent="TextAppearance.Medium">
<style name="TextAppearance.Widget.TwoLineRow.PrimaryText" parent="TextAppearance.Medium">
<item name="android:fontFamily">sans-serif-light</item>
</style>

Просмотреть файл

@ -109,22 +109,22 @@
<item name="android:ellipsize">middle</item>
</style>
<style name="Widget.TwoLinePageRow" />
<style name="Widget.TwoLineRow" />
<style name="Widget.TwoLinePageRow.Title">
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLinePageRow.Title</item>
<style name="Widget.TwoLineRow.PrimaryText">
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLineRow.PrimaryText</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">none</item>
</style>
<style name="Widget.TwoLinePageRow.Url">
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLinePageRow.Url</item>
<style name="Widget.TwoLineRow.SecondaryText">
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLineRow.SecondaryText</item>
<item name="android:includeFontPadding">false</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">middle</item>
</style>
<style name="Widget.BookmarkFolderView" parent="Widget.TwoLinePageRow.Title">
<style name="Widget.BookmarkFolderView" parent="Widget.TwoLineRow.PrimaryText">
<item name="android:paddingLeft">10dip</item>
<item name="android:drawablePadding">10dip</item>
<item name="android:drawableLeft">@drawable/bookmark_folder</item>
@ -143,7 +143,7 @@
<item name="android:orientation">vertical</item>
</style>
<style name="Widget.BookmarkItemView" parent="Widget.TwoLinePageRow"/>
<style name="Widget.BookmarkItemView" parent="Widget.TwoLineRow"/>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView"/>
@ -317,11 +317,11 @@
<item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.Widget.TwoLinePageRow" />
<style name="TextAppearance.Widget.TwoLineRow" />
<style name="TextAppearance.Widget.TwoLinePageRow.Title" parent="TextAppearance.Medium"/>
<style name="TextAppearance.Widget.TwoLineRow.PrimaryText" parent="TextAppearance.Medium"/>
<style name="TextAppearance.Widget.TwoLinePageRow.Url" parent="TextAppearance.Micro">
<style name="TextAppearance.Widget.TwoLineRow.SecondaryText" parent="TextAppearance.Micro">
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>

Просмотреть файл

@ -38,7 +38,7 @@ skip-if = processor == "x86"
[testInputUrlBar]
[testJarReader]
[testLinkContextMenu]
[testHomeListsProvider]
# [testHomeListsProvider] # see bug 952310
[testHomeProvider]
[testLoad]
[testMailToContextMenu]

Просмотреть файл

@ -448,8 +448,7 @@ var SelectionHandler = {
icon: "drawable://ab_paste",
action: function(aElement) {
ClipboardHelper.paste(aElement);
SelectionHandler._positionHandles();
SelectionHandler._updateMenu();
SelectionHandler._closeSelection();
},
order: 2,
selector: ClipboardHelper.pasteContext,

Просмотреть файл

@ -27,13 +27,15 @@ const SQL = {
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"dataset_id TEXT NOT NULL, " +
"url TEXT," +
"primary_text TEXT," +
"secondary_text TEXT" +
"title TEXT," +
"description TEXT," +
"image_url TEXT," +
"created INTEGER" +
")",
insertItem:
"INSERT INTO items (dataset_id, url) " +
"VALUES (:dataset_id, :url)",
"INSERT INTO items (dataset_id, url, title, description, image_url, created) " +
"VALUES (:dataset_id, :url, :title, :description, :image_url, :created)",
deleteFromDataset:
"DELETE FROM items WHERE dataset_id = :dataset_id"
@ -82,7 +84,11 @@ HomeStorage.prototype = {
// XXX: Directly pass item as params? More validation for item? Batch insert?
let params = {
dataset_id: this.datasetId,
url: item.url
url: item.url,
title: item.title,
description: item.description,
image_url: item.image_url,
created: Date.now()
};
yield db.executeCached(SQL.insertItem, params);
}

Просмотреть файл

@ -27,7 +27,6 @@ body {
}
#banner {
margin: 0 -10px;
min-height: 150px;
background-color: #bdc7ce;
}
@ -79,7 +78,7 @@ body {
}
#aboutLinks {
margin: 0 -10px 15px -10px;
margin: 0 0 15px 0;
padding: 0;
}