зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to mozilla-inbound
This commit is contained in:
Коммит
3e96086d0c
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "70f416f866858cb2068bffa31118fc4b15b482c9",
|
||||
"revision": "9ba62e3061abd5521ffbbee5386a0654f972f73b",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -6,34 +6,55 @@
|
|||
|
||||
#expand <menu id="__ID_PREFIX__charsetMenu"
|
||||
label="&charsetMenu.label;"
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenu.accesskey;"
|
||||
#endif
|
||||
oncommand="MultiplexHandler(event)"
|
||||
#ifdef OMIT_ACCESSKEYS
|
||||
onpopupshowing="CharsetMenu.build(event);"
|
||||
#else
|
||||
onpopupshowing="CharsetMenu.build(event, true);"
|
||||
#endif
|
||||
onpopupshown="UpdateMenus(event);">
|
||||
<menupopup>
|
||||
<menu label="&charsetMenuAutodet.label;"
|
||||
accesskey="&charsetMenuAutodet.accesskey;">
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.accesskey;"
|
||||
#endif
|
||||
>
|
||||
<menupopup>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.off"
|
||||
#expand id="__ID_PREFIX__chardet.off"
|
||||
label="&charsetMenuAutodet.off.label;"
|
||||
accesskey="&charsetMenuAutodet.off.accesskey;"/>
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.off.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.ja_parallel_state_machine"
|
||||
#expand id="__ID_PREFIX__chardet.ja_parallel_state_machine"
|
||||
label="&charsetMenuAutodet.ja.label;"
|
||||
accesskey="&charsetMenuAutodet.ja.accesskey;"/>
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.ja.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.ruprob"
|
||||
#expand id="__ID_PREFIX__chardet.ruprob"
|
||||
label="&charsetMenuAutodet.ru.label;"
|
||||
accesskey="&charsetMenuAutodet.ru.accesskey;"/>
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.ru.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.ukprob"
|
||||
#expand id="__ID_PREFIX__chardet.ukprob"
|
||||
label="&charsetMenuAutodet.uk.label;"
|
||||
accesskey="&charsetMenuAutodet.uk.accesskey;"/>
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.uk.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
|
|
|
@ -378,6 +378,7 @@
|
|||
<constructor><![CDATA[
|
||||
// Reading these immediately so nobody messes with them anymore:
|
||||
this._delegatingToolbar = this.getAttribute("toolbar-delegate");
|
||||
this._wasCollapsed = this.getAttribute("collapsed");
|
||||
// Leaving those in here to unbreak some code:
|
||||
if (document.readyState == "complete") {
|
||||
this._init();
|
||||
|
@ -409,8 +410,17 @@
|
|||
}
|
||||
|
||||
// pass the current set of children for comparison with placements:
|
||||
let children = [node.id for (node of this.childNodes)
|
||||
if (node.getAttribute("skipintoolbarset") != "true" && node.id)];
|
||||
let children = [];
|
||||
for (node of this.childNodes) {
|
||||
if (node.getAttribute("skipintoolbarset") != "true" && node.id) {
|
||||
// Force everything to be removable so that buildArea can chuck stuff
|
||||
// out if the user has customized things / we've been here before:
|
||||
if (!this._whiteListed.has(node.id)) {
|
||||
node.setAttribute("removable", "true");
|
||||
}
|
||||
children.push(node);
|
||||
}
|
||||
}
|
||||
CustomizableUI.registerToolbarNode(this, children);
|
||||
this.evictNodes();
|
||||
// We can't easily use |this| or strong bindings for the observer fn here
|
||||
|
@ -455,20 +465,28 @@
|
|||
}
|
||||
const kItemMaxWidth = 100;
|
||||
let oldParent = aNode.parentNode;
|
||||
aNode.setAttribute("removable", "true");
|
||||
|
||||
try {
|
||||
aNode.setAttribute("removable", "true");
|
||||
|
||||
let nodeWidth = aNode.getBoundingClientRect().width;
|
||||
if (nodeWidth == 0 || nodeWidth > kItemMaxWidth) {
|
||||
throw new Error(aNode.id + " is too big (" + nodeWidth +
|
||||
"px wide), moving to the palette");
|
||||
let movedOut = false;
|
||||
if (!this._wasCollapsed) {
|
||||
try {
|
||||
let nodeWidth = aNode.getBoundingClientRect().width;
|
||||
if (nodeWidth == 0 || nodeWidth > kItemMaxWidth) {
|
||||
throw new Error(aNode.id + " is too big (" + nodeWidth +
|
||||
"px wide), moving to the palette");
|
||||
}
|
||||
CustomizableUI.addWidgetToArea(aNode.id, this._delegatingToolbar);
|
||||
movedOut = true;
|
||||
} catch (ex) {
|
||||
// This will throw if the node is too big, or can't be moved there for
|
||||
// some reason. Report this:
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
CustomizableUI.addWidgetToArea(aNode.id, this._delegatingToolbar);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
// This will throw if the node is too big, or can't be moved there for
|
||||
// some reason. Try to remove it anyway:
|
||||
}
|
||||
|
||||
/* We won't have moved the widget if either the add-on bar was collapsed,
|
||||
* or if it was too wide to be inserted into the navbar. */
|
||||
if (!movedOut) {
|
||||
try {
|
||||
CustomizableUI.removeWidgetFromArea(aNode.id);
|
||||
} catch (ex) {
|
||||
|
|
|
@ -238,9 +238,19 @@ Editor.prototype = {
|
|||
|
||||
cm.on("focus", () => this.emit("focus"));
|
||||
cm.on("change", () => this.emit("change"));
|
||||
cm.on("gutterClick", (cm, line) => this.emit("gutterClick", line));
|
||||
cm.on("cursorActivity", (cm) => this.emit("cursorActivity"));
|
||||
|
||||
cm.on("gutterClick", (cm, line, gutter, ev) => {
|
||||
let head = { line: line, ch: 0 };
|
||||
let tail = { line: line, ch: this.getText(line).length };
|
||||
|
||||
// Shift-click on a gutter selects the whole line.
|
||||
if (ev.shiftKey)
|
||||
return void cm.setSelection(head, tail);
|
||||
|
||||
this.emit("gutterClick", line);
|
||||
});
|
||||
|
||||
win.CodeMirror.defineExtension("l10n", (name) => {
|
||||
return L10N.GetStringFromName(name);
|
||||
});
|
||||
|
|
|
@ -31,6 +31,13 @@ function test() {
|
|||
ed.dropSelection();
|
||||
is(ed.getSelection(), "", "dropSelection");
|
||||
|
||||
// Check that shift-click on a gutter selects the whole line (bug 919707)
|
||||
let iframe = win.document.querySelector("iframe");
|
||||
let gutter = iframe.contentWindow.document.querySelector(".CodeMirror-gutters");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown", shiftKey: true }, gutter, iframe.contentWindow);
|
||||
is(ed.getSelection(), "Hello.", "shift-click");
|
||||
|
||||
teardown(ed, win);
|
||||
});
|
||||
}
|
|
@ -82,7 +82,7 @@ const kPinned = [
|
|||
];
|
||||
|
||||
this.CharsetMenu = Object.freeze({
|
||||
build: function BuildCharsetMenu(event) {
|
||||
build: function BuildCharsetMenu(event, showAccessKeys) {
|
||||
let parent = event.target;
|
||||
if (parent.lastChild.localName != "menuseparator") {
|
||||
// Detector menu or charset menu already built
|
||||
|
@ -100,11 +100,13 @@ this.CharsetMenu = Object.freeze({
|
|||
// Localization error but put *something* in the menu to recover.
|
||||
menuItem.setAttribute("label", encoding);
|
||||
}
|
||||
try {
|
||||
menuItem.setAttribute("accesskey",
|
||||
gBundle.GetStringFromName(encoding + ".key"));
|
||||
} catch (e) {
|
||||
// Some items intentionally don't have an accesskey
|
||||
if (showAccessKeys) {
|
||||
try {
|
||||
menuItem.setAttribute("accesskey",
|
||||
gBundle.GetStringFromName(encoding + ".key"));
|
||||
} catch (e) {
|
||||
// Some items intentionally don't have an accesskey
|
||||
}
|
||||
}
|
||||
menuItem.setAttribute("id", "charset." + encoding);
|
||||
return menuItem;
|
||||
|
|
|
@ -39,5 +39,38 @@ public class AboutPages {
|
|||
}
|
||||
return url.startsWith(READER);
|
||||
}
|
||||
|
||||
private static final String[] DEFAULT_ICON_PAGES = new String[] {
|
||||
HOME,
|
||||
|
||||
ADDONS,
|
||||
CONFIG,
|
||||
DOWNLOADS,
|
||||
FIREFOX,
|
||||
HEALTHREPORT,
|
||||
UPDATER
|
||||
};
|
||||
|
||||
/**
|
||||
* Callers must not modify the returned array.
|
||||
*/
|
||||
public static String[] getDefaultIconPages() {
|
||||
return DEFAULT_ICON_PAGES;
|
||||
}
|
||||
|
||||
public static boolean isDefaultIconPage(final String url) {
|
||||
if (url == null ||
|
||||
!url.startsWith("about:")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: it'd be quicker to not compare the "about:" part every time.
|
||||
for (int i = 0; i < DEFAULT_ICON_PAGES.length; ++i) {
|
||||
if (DEFAULT_ICON_PAGES[i].equals(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -197,10 +197,24 @@ abstract public class BrowserApp extends GeckoApp
|
|||
|
||||
@Override
|
||||
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
||||
if (tab == null) {
|
||||
// Only RESTORED is allowed a null tab: it's the only event that
|
||||
// isn't tied to a specific tab.
|
||||
if (msg != Tabs.TabEvents.RESTORED) {
|
||||
throw new IllegalArgumentException("onTabChanged:" + msg + " must specify a tab.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(LOGTAG, "BrowserApp.onTabChanged: " + tab.getId() + ": " + msg);
|
||||
switch(msg) {
|
||||
// We don't get a LOCATION_CHANGE event for the first about:home
|
||||
// load, because the previous and current URIs are the
|
||||
// same. That means it's OK to trigger a new favicon load
|
||||
// at this point.
|
||||
case LOCATION_CHANGE:
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
maybeCancelFaviconLoad(tab);
|
||||
loadFavicon(tab);
|
||||
}
|
||||
// fall through
|
||||
case SELECTED:
|
||||
|
@ -241,9 +255,6 @@ abstract public class BrowserApp extends GeckoApp
|
|||
invalidateOptionsMenu();
|
||||
}
|
||||
break;
|
||||
case PAGE_SHOW:
|
||||
loadFavicon(tab);
|
||||
break;
|
||||
case LINK_FAVICON:
|
||||
// If tab is not loading and the favicon is updated, we
|
||||
// want to load the image straight away. If tab is still
|
||||
|
@ -1229,7 +1240,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
|
||||
@Override
|
||||
public void addTab() {
|
||||
Tabs.getInstance().loadUrl(AboutPages.HOME, Tabs.LOADURL_NEW_TAB);
|
||||
super.loadHomePage(Tabs.LOADURL_NEW_TAB);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1401,7 +1412,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
}
|
||||
|
||||
private void openReadingList() {
|
||||
Tabs.getInstance().loadUrl(AboutPages.HOME, Tabs.LOADURL_READING_LIST);
|
||||
super.loadHomePage(Tabs.LOADURL_READING_LIST);
|
||||
}
|
||||
|
||||
/* Favicon stuff. */
|
||||
|
@ -1428,10 +1439,13 @@ abstract public class BrowserApp extends GeckoApp
|
|||
private void maybeCancelFaviconLoad(Tab tab) {
|
||||
int faviconLoadId = tab.getFaviconLoadId();
|
||||
|
||||
// Cancel pending favicon load task
|
||||
Favicons.cancelFaviconLoad(faviconLoadId);
|
||||
if (Favicons.NOT_LOADING == faviconLoadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset favicon load state
|
||||
// Cancel load task and reset favicon load state if it wasn't already
|
||||
// in NOT_LOADING state.
|
||||
Favicons.cancelFaviconLoad(faviconLoadId);
|
||||
tab.setFaviconLoadId(Favicons.NOT_LOADING);
|
||||
}
|
||||
|
||||
|
|
|
@ -1354,8 +1354,8 @@ abstract public class GeckoApp
|
|||
if (url == null) {
|
||||
if (!mShouldRestore) {
|
||||
// Show about:home if we aren't restoring previous session and
|
||||
// there's no external URL
|
||||
Tab tab = Tabs.getInstance().loadUrl(AboutPages.HOME, Tabs.LOADURL_NEW_TAB);
|
||||
// there's no external URL.
|
||||
loadHomePage(Tabs.LOADURL_NEW_TAB);
|
||||
}
|
||||
} else {
|
||||
// If given an external URL, load it
|
||||
|
@ -1364,6 +1364,14 @@ abstract public class GeckoApp
|
|||
}
|
||||
}
|
||||
|
||||
protected Tab loadHomePage() {
|
||||
return loadHomePage(Tabs.LOADURL_NONE);
|
||||
}
|
||||
|
||||
protected Tab loadHomePage(int flags) {
|
||||
return Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
mInitialized = true;
|
||||
|
||||
|
|
|
@ -627,6 +627,12 @@ public class Tab {
|
|||
final String uri = message.getString("uri");
|
||||
final String oldUrl = getURL();
|
||||
mEnteringReaderMode = ReaderModeUtils.isEnteringReaderMode(oldUrl, uri);
|
||||
|
||||
if (TextUtils.equals(oldUrl, uri)) {
|
||||
Log.d(LOGTAG, "Ignoring location change event: URIs are the same.");
|
||||
return;
|
||||
}
|
||||
|
||||
updateURL(uri);
|
||||
updateUserSearch(message.getString("userSearch"));
|
||||
|
||||
|
@ -639,7 +645,13 @@ public class Tab {
|
|||
}
|
||||
|
||||
setContentType(message.getString("contentType"));
|
||||
|
||||
// We can unconditionally clear the favicon here: we already
|
||||
// short-circuited for both cases in which this was a (pseudo-)
|
||||
// spurious location change, so we're definitely loading a new page.
|
||||
// The same applies to all of the other fields we're wiping out.
|
||||
clearFavicon();
|
||||
|
||||
setHasFeeds(false);
|
||||
updateTitle(null);
|
||||
updateIdentityData(null);
|
||||
|
|
|
@ -382,6 +382,7 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
Log.d(LOGTAG, "handleMessage: " + event);
|
||||
try {
|
||||
if (event.equals("Session:RestoreEnd")) {
|
||||
notifyListeners(null, TabEvents.RESTORED);
|
||||
|
@ -562,6 +563,11 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
// Throws if not initialized.
|
||||
public void notifyListeners(final Tab tab, final TabEvents msg, final Object data) {
|
||||
if (tab == null &&
|
||||
msg != TabEvents.RESTORED) {
|
||||
throw new IllegalArgumentException("onTabChanged:" + msg + " must specify a tab.");
|
||||
}
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -660,8 +666,8 @@ public class Tabs implements GeckoEventListener {
|
|||
*
|
||||
* @param url URL of page to load, or search term used if searchEngine is given
|
||||
*/
|
||||
public void loadUrl(String url) {
|
||||
loadUrl(url, LOADURL_NONE);
|
||||
public Tab loadUrl(String url) {
|
||||
return loadUrl(url, LOADURL_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -731,13 +737,34 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Load", args.toString()));
|
||||
|
||||
if ((added != null) && !delayLoad && !background) {
|
||||
if (added == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!delayLoad && !background) {
|
||||
selectTab(added.getId());
|
||||
}
|
||||
|
||||
// TODO: surely we could just fetch *any* cached icon?
|
||||
if (AboutPages.isDefaultIconPage(url)) {
|
||||
Log.d(LOGTAG, "Setting about: tab favicon inline.");
|
||||
added.updateFavicon(getAboutPageFavicon(url));
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* These favicons are only used for the URL bar, so
|
||||
* we fetch with that size.
|
||||
*
|
||||
* This method completes on the calling thread.
|
||||
*/
|
||||
private Bitmap getAboutPageFavicon(final String url) {
|
||||
int faviconSize = Math.round(mAppContext.getResources().getDimension(R.dimen.browser_toolbar_favicon_size));
|
||||
return Favicons.getCachedFaviconForSize(url, faviconSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the url as a new tab, and mark the selected tab as its "parent".
|
||||
*
|
||||
|
|
|
@ -6,23 +6,28 @@
|
|||
package org.mozilla.gecko.favicons;
|
||||
|
||||
import org.mozilla.gecko.AboutPages;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.favicons.cache.FaviconCache;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.GeckoJarReader;
|
||||
import org.mozilla.gecko.util.NonEvictingLruCache;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -32,6 +37,9 @@ import java.util.Set;
|
|||
public class Favicons {
|
||||
private static final String LOGTAG = "GeckoFavicons";
|
||||
|
||||
// A magic URL representing the app's own favicon, used for about: pages.
|
||||
private static final String BUILT_IN_FAVICON_URL = "about:favicon";
|
||||
|
||||
// Size of the favicon bitmap cache, in bytes (Counting payload only).
|
||||
public static final int FAVICON_CACHE_SIZE_BYTES = 512 * 1024;
|
||||
|
||||
|
@ -97,6 +105,20 @@ public class Favicons {
|
|||
return NOT_LOADING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns a non-null Bitmap if the entire path is cached -- the
|
||||
* page URL to favicon URL, and the favicon URL to in-memory bitmaps.
|
||||
*
|
||||
* Returns null otherwise.
|
||||
*/
|
||||
public static Bitmap getCachedFaviconForSize(final String pageURL, int targetSize) {
|
||||
final String faviconURL = sPageURLMappings.get(pageURL);
|
||||
if (faviconURL == null) {
|
||||
return null;
|
||||
}
|
||||
return getSizedFaviconFromCache(faviconURL, targetSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Favicon as close as possible to the target dimensions for the URL provided.
|
||||
* If a result is instantly available from the cache, it is returned and the listener is invoked.
|
||||
|
@ -113,8 +135,13 @@ public class Favicons {
|
|||
* LOADED if the value could be dispatched on the current thread.
|
||||
*/
|
||||
public static int getFaviconForSize(String pageURL, String faviconURL, int targetSize, int flags, OnFaviconLoadedListener listener) {
|
||||
// If there's no favicon URL given, try and hit the cache with the default one.
|
||||
// Do we know the favicon URL for this page already?
|
||||
String cacheURL = faviconURL;
|
||||
if (cacheURL == null) {
|
||||
cacheURL = sPageURLMappings.get(pageURL);
|
||||
}
|
||||
|
||||
// If there's no favicon URL given, try and hit the cache with the default one.
|
||||
if (cacheURL == null) {
|
||||
cacheURL = guessDefaultFaviconURL(pageURL);
|
||||
}
|
||||
|
@ -262,8 +289,8 @@ public class Favicons {
|
|||
sFaviconsCache.putSingleFavicon(pageUrl, image);
|
||||
}
|
||||
|
||||
public static void putFaviconsInMemCache(String pageUrl, Iterator<Bitmap> images) {
|
||||
sFaviconsCache.putFavicons(pageUrl, images);
|
||||
public static void putFaviconsInMemCache(String pageUrl, Iterator<Bitmap> images, boolean permanently) {
|
||||
sFaviconsCache.putFavicons(pageUrl, images, permanently);
|
||||
}
|
||||
|
||||
public static void clearMemCache() {
|
||||
|
@ -328,16 +355,49 @@ public class Favicons {
|
|||
* @param context A reference to the GeckoApp instance.
|
||||
*/
|
||||
public static void attachToContext(Context context) throws Exception {
|
||||
final Resources res = context.getResources();
|
||||
sContext = context;
|
||||
|
||||
// Decode the default Favicon ready for use.
|
||||
sDefaultFavicon = BitmapFactory.decodeResource(context.getResources(), R.drawable.favicon);
|
||||
sDefaultFavicon = BitmapFactory.decodeResource(res, R.drawable.favicon);
|
||||
if (sDefaultFavicon == null) {
|
||||
throw new Exception("Null default favicon was returned from the resources system!");
|
||||
}
|
||||
|
||||
sDefaultFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.favicon_bg);
|
||||
sFaviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, context.getResources().getDimensionPixelSize(R.dimen.favicon_largest_interesting_size));
|
||||
sDefaultFaviconSize = res.getDimensionPixelSize(R.dimen.favicon_bg);
|
||||
sFaviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, res.getDimensionPixelSize(R.dimen.favicon_largest_interesting_size));
|
||||
|
||||
// Initialize page mappings for each of our special pages.
|
||||
for (String url : AboutPages.getDefaultIconPages()) {
|
||||
sPageURLMappings.putWithoutEviction(url, BUILT_IN_FAVICON_URL);
|
||||
}
|
||||
|
||||
// Load and cache the built-in favicon in each of its sizes.
|
||||
// TODO: don't open the zip twice!
|
||||
ArrayList<Bitmap> toInsert = new ArrayList<Bitmap>(2);
|
||||
toInsert.add(loadBrandingBitmap(context, "favicon64.png"));
|
||||
toInsert.add(loadBrandingBitmap(context, "favicon32.png"));
|
||||
putFaviconsInMemCache(BUILT_IN_FAVICON_URL, toInsert.iterator(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a string like:
|
||||
* "jar:jar:file:///data/app/org.mozilla.firefox-1.apk!/assets/omni.ja!/chrome/chrome/content/branding/favicon64.png"
|
||||
*/
|
||||
private static String getBrandingBitmapPath(Context context, String name) {
|
||||
final String apkPath = context.getPackageResourcePath();
|
||||
return "jar:jar:" + new File(apkPath).toURI() + "!/" +
|
||||
AppConstants.OMNIJAR_NAME + "!/" +
|
||||
"chrome/chrome/content/branding/" + name;
|
||||
}
|
||||
|
||||
private static Bitmap loadBrandingBitmap(Context context, String name) {
|
||||
Bitmap b = GeckoJarReader.getBitmap(context.getResources(),
|
||||
getBrandingBitmapPath(context, name));
|
||||
if (b == null) {
|
||||
throw new IllegalStateException("Bitmap " + name + " missing from JAR!");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,9 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
* The collection of FaviconsForURL objects currently in the cache is stored in mBackingMap, keyed
|
||||
* by favicon URL.
|
||||
*
|
||||
* A second map exists for permanent cache entries -- ones that are never expired. These entries
|
||||
* are assumed to be disjoint from those in the normal cache, and this map is checked first.
|
||||
*
|
||||
* FaviconsForURL provides a method for obtaining the smallest icon larger than a given size - the
|
||||
* most appropriate icon for a particular size.
|
||||
* It also distinguishes between "primary" favicons (Ones that have merely been extracted from a
|
||||
|
@ -106,6 +109,9 @@ public class FaviconCache {
|
|||
// for the least larger payload currently present.
|
||||
private final ConcurrentHashMap<String, FaviconsForURL> mBackingMap = new ConcurrentHashMap<String, FaviconsForURL>();
|
||||
|
||||
// And the same, but never evicted.
|
||||
private final ConcurrentHashMap<String, FaviconsForURL> mPermanentBackingMap = new ConcurrentHashMap<String, FaviconsForURL>();
|
||||
|
||||
// A linked list used to implement a queue, defining the LRU properties of the cache. Elements
|
||||
// contained within the various FaviconsForURL objects are held here, the least recently used
|
||||
// of which at the end of the list. When space needs to be reclaimed, the appropriate bitmap is
|
||||
|
@ -117,7 +123,7 @@ public class FaviconCache {
|
|||
// the primary bitmap most suited to the requested size (in cases where multiple primary bitmaps
|
||||
// are provided by the underlying file format).
|
||||
|
||||
// Current size, in bytes, of the bitmap data present in the cache.
|
||||
// Current size, in bytes, of the bitmap data present in the LRU cache.
|
||||
private final AtomicInteger mCurrentSize = new AtomicInteger(0);
|
||||
|
||||
// The maximum quantity, in bytes, of bitmap data which may be stored in the cache.
|
||||
|
@ -216,6 +222,8 @@ public class FaviconCache {
|
|||
|
||||
try {
|
||||
// If we don't have it in the cache, it certainly isn't a known failure.
|
||||
// Non-evictable favicons are never failed, so we don't need to
|
||||
// check mPermanentBackingMap.
|
||||
if (!mBackingMap.containsKey(faviconURL)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -304,18 +312,24 @@ public class FaviconCache {
|
|||
boolean doingWrites = false;
|
||||
boolean shouldComputeColour = false;
|
||||
boolean isAborting = false;
|
||||
boolean wasPermanent = false;
|
||||
FaviconsForURL container;
|
||||
final Bitmap newBitmap;
|
||||
final FaviconsForURL container;
|
||||
|
||||
startRead();
|
||||
|
||||
try {
|
||||
if (!mBackingMap.containsKey(faviconURL)) {
|
||||
return null;
|
||||
container = mPermanentBackingMap.get(faviconURL);
|
||||
if (container == null) {
|
||||
container = mBackingMap.get(faviconURL);
|
||||
if (container == null) {
|
||||
// We don't have it!
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
wasPermanent = true;
|
||||
}
|
||||
|
||||
container = mBackingMap.get(faviconURL);
|
||||
|
||||
FaviconCacheElement cacheElement;
|
||||
|
||||
int cacheElementIndex = container.getNextHighestIndex(targetSize);
|
||||
|
@ -412,9 +426,10 @@ public class FaviconCache {
|
|||
// This way, subsequent requests hit straight away.
|
||||
FaviconCacheElement newElement = container.addSecondary(newBitmap, targetSize);
|
||||
|
||||
setMostRecentlyUsed(newElement);
|
||||
|
||||
mCurrentSize.addAndGet(newElement.sizeOf());
|
||||
if (!wasPermanent) {
|
||||
setMostRecentlyUsed(newElement);
|
||||
mCurrentSize.addAndGet(newElement.sizeOf());
|
||||
}
|
||||
} finally {
|
||||
finishWrite();
|
||||
}
|
||||
|
@ -432,14 +447,18 @@ public class FaviconCache {
|
|||
startRead();
|
||||
|
||||
try {
|
||||
if (!mBackingMap.containsKey(key)) {
|
||||
FaviconsForURL element = mPermanentBackingMap.get(key);
|
||||
if (element == null) {
|
||||
element = mBackingMap.get(key);
|
||||
}
|
||||
|
||||
if (element == null) {
|
||||
Log.w(LOGTAG, "Cannot compute dominant color of non-cached favicon. Cache fullness " +
|
||||
mCurrentSize.get() + '/' + mMaxSizeBytes);
|
||||
finishRead();
|
||||
return 0xFFFFFF;
|
||||
}
|
||||
|
||||
FaviconsForURL element = mBackingMap.get(key);
|
||||
|
||||
return element.ensureDominantColor();
|
||||
} finally {
|
||||
|
@ -543,8 +562,9 @@ public class FaviconCache {
|
|||
*
|
||||
* @param faviconURL The URL from which the favicons originate.
|
||||
* @param favicons A List of favicons decoded from this URL.
|
||||
* @param permanently If true, the added favicons are never subject to eviction.
|
||||
*/
|
||||
public void putFavicons(String faviconURL, Iterator<Bitmap> favicons) {
|
||||
public void putFavicons(String faviconURL, Iterator<Bitmap> favicons, boolean permanently) {
|
||||
// We don't know how many icons we'll have - let's just take a guess.
|
||||
FaviconsForURL toInsert = new FaviconsForURL(5 * NUM_FAVICON_SIZES);
|
||||
int sizeGained = 0;
|
||||
|
@ -567,8 +587,10 @@ public class FaviconCache {
|
|||
// without taking the write lock, via the magic of the reordering semaphore.
|
||||
mReorderingSemaphore.acquireUninterruptibly();
|
||||
try {
|
||||
for (FaviconCacheElement newElement : toInsert.mFavicons) {
|
||||
mOrdering.offer(newElement);
|
||||
if (!permanently) {
|
||||
for (FaviconCacheElement newElement : toInsert.mFavicons) {
|
||||
mOrdering.offer(newElement);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
abortingRead = true;
|
||||
|
@ -585,10 +607,14 @@ public class FaviconCache {
|
|||
}
|
||||
|
||||
try {
|
||||
mCurrentSize.addAndGet(sizeGained);
|
||||
if (permanently) {
|
||||
mPermanentBackingMap.put(faviconURL, toInsert);
|
||||
} else {
|
||||
mCurrentSize.addAndGet(sizeGained);
|
||||
|
||||
// Update the value in the LruCache...
|
||||
recordRemoved(mBackingMap.put(faviconURL, toInsert));
|
||||
// Update the value in the LruCache...
|
||||
recordRemoved(mBackingMap.put(faviconURL, toInsert));
|
||||
}
|
||||
} finally {
|
||||
finishWrite();
|
||||
}
|
||||
|
@ -631,10 +657,12 @@ public class FaviconCache {
|
|||
public void evictAll() {
|
||||
startWrite();
|
||||
|
||||
// Note that we neither clear, nor track the size of, the permanent map.
|
||||
try {
|
||||
mCurrentSize.set(0);
|
||||
mBackingMap.clear();
|
||||
mOrdering.clear();
|
||||
|
||||
} finally {
|
||||
finishWrite();
|
||||
}
|
||||
|
|
|
@ -115,8 +115,12 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
private ShapedButton mTabs;
|
||||
private ImageButton mBack;
|
||||
private ImageButton mForward;
|
||||
private ImageButton mFavicon;
|
||||
private ImageButton mStop;
|
||||
|
||||
// To de-bounce sets.
|
||||
private Bitmap mLastFavicon;
|
||||
private ImageButton mFavicon;
|
||||
|
||||
private ImageButton mSiteSecurity;
|
||||
private PageActionLayout mPageActionLayout;
|
||||
private Animation mProgressSpinner;
|
||||
|
@ -428,7 +432,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
|
||||
mFavicon.setOnClickListener(faviconListener);
|
||||
mSiteSecurity.setOnClickListener(faviconListener);
|
||||
|
||||
|
||||
mStop.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -532,6 +536,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
|
||||
@Override
|
||||
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
||||
Log.d(LOGTAG, "onTabChanged: " + msg);
|
||||
final Tabs tabs = Tabs.getInstance();
|
||||
|
||||
// These conditions are split into three phases:
|
||||
|
@ -583,7 +588,12 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
case LOCATION_CHANGE:
|
||||
// A successful location change will cause Tab to notify
|
||||
// us of a title change, so we don't update the title here.
|
||||
refresh();
|
||||
// And there's no point in refreshing the UI
|
||||
// if the page is the same.
|
||||
final String oldURL = (String) data;
|
||||
if (!TextUtils.equals(oldURL, tab.getURL())) {
|
||||
refresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
|
@ -741,12 +751,15 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
}
|
||||
|
||||
public void setProgressVisibility(boolean visible) {
|
||||
Log.d(LOGTAG, "setProgressVisibility: " + visible);
|
||||
// The "Throbber start" and "Throbber stop" log messages in this method
|
||||
// are needed by S1/S2 tests (http://mrcote.info/phonedash/#).
|
||||
// See discussion in Bug 804457. Bug 805124 tracks paring these down.
|
||||
if (visible) {
|
||||
mFavicon.setImageResource(R.drawable.progress_spinner);
|
||||
//To stop the glitch caused by mutiple start() calls.
|
||||
mLastFavicon = null;
|
||||
|
||||
// To stop the glitch caused by multiple start() calls.
|
||||
if (!mSpinnerVisible) {
|
||||
setPageActionVisibility(true);
|
||||
mFavicon.setAnimation(mProgressSpinner);
|
||||
|
@ -756,8 +769,9 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber start");
|
||||
} else {
|
||||
Tab selectedTab = Tabs.getInstance().getSelectedTab();
|
||||
if (selectedTab != null)
|
||||
if (selectedTab != null) {
|
||||
setFavicon(selectedTab.getFavicon());
|
||||
}
|
||||
|
||||
if (mSpinnerVisible) {
|
||||
setPageActionVisibility(false);
|
||||
|
@ -868,15 +882,15 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
setContentDescription(title != null ? title : mTitle.getHint());
|
||||
}
|
||||
|
||||
// Sets the toolbar title according to the selected tab, obeying the mShowUrl prference.
|
||||
// Sets the toolbar title according to the selected tab, obeying the mShowUrl preference.
|
||||
private void updateTitle() {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
// Keep the title unchanged if there's no selected tab, or if the tab is entering reader mode.
|
||||
if (tab == null || tab.isEnteringReaderMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String url = tab.getURL();
|
||||
final String url = tab.getURL();
|
||||
|
||||
if (!isEditing()) {
|
||||
mUrlEditLayout.setText(url);
|
||||
|
@ -923,8 +937,17 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
}
|
||||
|
||||
private void setFavicon(Bitmap image) {
|
||||
if (Tabs.getInstance().getSelectedTab().getState() == Tab.STATE_LOADING)
|
||||
Log.d(LOGTAG, "setFavicon(" + image + ")");
|
||||
if (Tabs.getInstance().getSelectedTab().getState() == Tab.STATE_LOADING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (image == mLastFavicon) {
|
||||
Log.d(LOGTAG, "Ignoring favicon set: new favicon is identical to previous favicon.");
|
||||
return;
|
||||
}
|
||||
|
||||
mLastFavicon = image; // Cache the original so we can debounce without scaling.
|
||||
|
||||
if (image != null) {
|
||||
image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false);
|
||||
|
@ -933,7 +956,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
mFavicon.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setSecurityMode(String mode) {
|
||||
int imageLevel = SiteIdentityPopup.getSecurityImageLevel(mode);
|
||||
mSiteSecurity.setImageLevel(imageLevel);
|
||||
|
@ -1571,6 +1594,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
Log.d(LOGTAG, "handleMessage: " + event);
|
||||
if (event.equals("Reader:Click")) {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&abouthome.title;</title>
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
|
|
@ -4388,11 +4388,16 @@ var BrowserEventHandler = {
|
|||
element = ElementTouchHelper.anyElementFromPoint(x, y);
|
||||
}
|
||||
|
||||
// Was the element already focused before it was clicked?
|
||||
let isFocused = (element == BrowserApp.getFocusedInput(BrowserApp.selectedBrowser, true));
|
||||
|
||||
this._sendMouseEvent("mousemove", element, x, y);
|
||||
this._sendMouseEvent("mousedown", element, x, y);
|
||||
this._sendMouseEvent("mouseup", element, x, y);
|
||||
|
||||
SelectionHandler.attachCaret(element);
|
||||
// If the element was previously focused, show the caret attached to it.
|
||||
if (isFocused)
|
||||
SelectionHandler.attachCaret(element);
|
||||
|
||||
// scrollToFocusedInput does its own checks to find out if an element should be zoomed into
|
||||
BrowserApp.scrollToFocusedInput(BrowserApp.selectedBrowser);
|
||||
|
|
|
@ -300,9 +300,9 @@ select[disabled] > button {
|
|||
*:-moz-any-link:active,
|
||||
*[role=button]:active,
|
||||
button:not([disabled]):active,
|
||||
input:not([disabled]):active,
|
||||
input:not(:focus):not([disabled]):active,
|
||||
select:not([disabled]):active,
|
||||
textarea:not([disabled]):active,
|
||||
textarea:not(:focus):not([disabled]):active,
|
||||
option:active,
|
||||
label:active,
|
||||
xul|menulist:active {
|
||||
|
|
Загрузка…
Ссылка в новой задаче