mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c.
This commit is contained in:
commit
fb74bca91e
@ -4653,7 +4653,9 @@ let TabStateCache = {
|
||||
*/
|
||||
get: function(aKey) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
return this._data.get(key);
|
||||
let result = this._data.get(key);
|
||||
TabStateCacheTelemetry.recordAccess(!!result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -4672,6 +4674,7 @@ let TabStateCache = {
|
||||
* Delete all tab data.
|
||||
*/
|
||||
clear: function() {
|
||||
TabStateCacheTelemetry.recordClear();
|
||||
this._data.clear();
|
||||
},
|
||||
|
||||
@ -4689,6 +4692,7 @@ let TabStateCache = {
|
||||
if (data) {
|
||||
data[aField] = aValue;
|
||||
}
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
_normalizeToBrowser: function(aKey) {
|
||||
@ -4702,3 +4706,68 @@ let TabStateCache = {
|
||||
throw new TypeError("Key is neither a tab nor a browser: " + nodeName);
|
||||
}
|
||||
};
|
||||
|
||||
let TabStateCacheTelemetry = {
|
||||
// Total number of hits during the session
|
||||
_hits: 0,
|
||||
// Total number of misses during the session
|
||||
_misses: 0,
|
||||
// Total number of clears during the session
|
||||
_clears: 0,
|
||||
// |true| once we have been initialized
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Record a cache access.
|
||||
*
|
||||
* @param {boolean} isHit If |true|, the access was a hit, otherwise
|
||||
* a miss.
|
||||
*/
|
||||
recordAccess: function(isHit) {
|
||||
this._init();
|
||||
if (isHit) {
|
||||
++this._hits;
|
||||
} else {
|
||||
++this._misses;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Record a cache clear
|
||||
*/
|
||||
recordClear: function() {
|
||||
this._init();
|
||||
++this._clears;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the telemetry.
|
||||
*/
|
||||
_init: function() {
|
||||
if (this._initialized) {
|
||||
// Avoid double initialization
|
||||
return;
|
||||
}
|
||||
Services.obs.addObserver(this, "profile-before-change", false);
|
||||
},
|
||||
|
||||
observe: function() {
|
||||
Services.obs.removeObserver(this, "profile-before-change");
|
||||
|
||||
// Record hit/miss rate
|
||||
let accesses = this._hits + this._misses;
|
||||
if (accesses == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fillHistogram("HIT_RATE", this._hits, accesses);
|
||||
this._fillHistogram("CLEAR_RATIO", this._clears, accesses);
|
||||
},
|
||||
|
||||
_fillHistogram: function(suffix, positive, total) {
|
||||
let PREFIX = "FX_SESSION_RESTORE_TABSTATECACHE_";
|
||||
let histo = Services.telemetry.getHistogramById(PREFIX + suffix);
|
||||
let rate = Math.floor( ( positive * 100 ) / total );
|
||||
histo.add(rate);
|
||||
}
|
||||
};
|
||||
|
@ -94,6 +94,7 @@ var TouchModule = {
|
||||
// capture phase events
|
||||
window.addEventListener("CancelTouchSequence", this, true);
|
||||
window.addEventListener("dblclick", this, true);
|
||||
window.addEventListener("keydown", this, true);
|
||||
|
||||
// bubble phase
|
||||
window.addEventListener("contextmenu", this, false);
|
||||
@ -156,11 +157,41 @@ var TouchModule = {
|
||||
}, 50);
|
||||
}
|
||||
break;
|
||||
case "keydown":
|
||||
this._handleKeyDown(aEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleKeyDown: function _handleKeyDown(aEvent) {
|
||||
const TABKEY = 9;
|
||||
if (aEvent.keyCode == TABKEY && !InputSourceHelper.isPrecise) {
|
||||
if (Util.isEditable(aEvent.target) &&
|
||||
aEvent.target.selectionStart != aEvent.target.selectionEnd) {
|
||||
SelectionHelperUI.closeEditSession(false);
|
||||
}
|
||||
setTimeout(function() {
|
||||
let element = Browser.selectedBrowser.contentDocument.activeElement;
|
||||
// We only want to attach monocles if we have an input, text area,
|
||||
// there is selection, and the target element changed.
|
||||
// Sometimes the target element won't change even though selection is
|
||||
// cleared because of focus outside the browser.
|
||||
if (Util.isEditable(element) &&
|
||||
!SelectionHelperUI.isActive &&
|
||||
element.selectionStart != element.selectionEnd &&
|
||||
// not e10s friendly
|
||||
aEvent.target != element) {
|
||||
let rect = element.getBoundingClientRect();
|
||||
SelectionHelperUI.attachEditSession(Browser.selectedBrowser,
|
||||
rect.left + rect.width / 2,
|
||||
rect.top + rect.height / 2);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
},
|
||||
|
||||
sample: function sample(aTimeStamp) {
|
||||
this._waitingForPaint = false;
|
||||
},
|
||||
|
@ -1441,9 +1441,11 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
void filterEditingMode(String searchTerm, AutocompleteHandler handler) {
|
||||
if (TextUtils.isEmpty(searchTerm)) {
|
||||
mHomePager.setVisibility(View.VISIBLE);
|
||||
hideBrowserSearch();
|
||||
} else {
|
||||
showBrowserSearch();
|
||||
mHomePager.setVisibility(View.INVISIBLE);
|
||||
mBrowserSearch.filter(searchTerm, handler);
|
||||
}
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ public class GeckoAccessibility {
|
||||
default:
|
||||
info.setParent(host);
|
||||
info.setSource(host, virtualDescendantId);
|
||||
info.setVisibleToUser(true);
|
||||
info.setVisibleToUser(host.isFocused());
|
||||
info.setPackageName(GeckoAppShell.getContext().getPackageName());
|
||||
info.setClassName(host.getClass().getName());
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
|
||||
|
@ -227,6 +227,7 @@ FENNEC_JAVA_FILES = \
|
||||
home/HomeListView.java \
|
||||
home/HomePager.java \
|
||||
home/HomePagerTabStrip.java \
|
||||
home/HomeBanner.java \
|
||||
home/FadedTextView.java \
|
||||
home/FaviconsLoader.java \
|
||||
home/LastTabsPage.java \
|
||||
@ -481,6 +482,7 @@ RES_LAYOUT = \
|
||||
res/layout/home_pager.xml \
|
||||
res/layout/home_reading_list_page.xml \
|
||||
res/layout/home_search_item_row.xml \
|
||||
res/layout/home_banner.xml \
|
||||
res/layout/home_suggestion_prompt.xml \
|
||||
res/layout/web_app.xml \
|
||||
res/layout/launch_app_list.xml \
|
||||
@ -1113,6 +1115,7 @@ RES_DRAWABLE += \
|
||||
res/drawable/handle_start_level.xml \
|
||||
res/drawable/home_history_tabs_indicator.xml \
|
||||
res/drawable/home_page_title_background.xml \
|
||||
res/drawable/home_banner.xml \
|
||||
res/drawable/ic_menu_back.xml \
|
||||
res/drawable/ic_menu_desktop_mode_off.xml \
|
||||
res/drawable/ic_menu_desktop_mode_on.xml \
|
||||
|
@ -8,6 +8,9 @@ package org.mozilla.gecko.home;
|
||||
import org.mozilla.gecko.Favicons;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator.Property;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
|
||||
import org.mozilla.gecko.db.BrowserContract.Thumbnails;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
@ -41,7 +44,9 @@ import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -76,6 +81,9 @@ public class BookmarksPage extends HomeFragment {
|
||||
// Grid of top bookmarks.
|
||||
private TopBookmarksView mTopBookmarks;
|
||||
|
||||
// Banner to show snippets.
|
||||
private HomeBanner mBanner;
|
||||
|
||||
// Adapter for list of bookmarks.
|
||||
private BookmarksListAdapter mListAdapter;
|
||||
|
||||
@ -91,14 +99,22 @@ public class BookmarksPage extends HomeFragment {
|
||||
// Listener for pinning bookmarks.
|
||||
private PinBookmarkListener mPinBookmarkListener;
|
||||
|
||||
// Raw Y value of the last event that happened on the list view.
|
||||
private float mListTouchY = -1;
|
||||
|
||||
// Scrolling direction of the banner.
|
||||
private boolean mSnapBannerToTop;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
BookmarksListView list = (BookmarksListView) inflater.inflate(R.layout.home_bookmarks_page, container, false);
|
||||
final View view = inflater.inflate(R.layout.home_bookmarks_page, container, false);
|
||||
|
||||
mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
|
||||
|
||||
mTopBookmarks = new TopBookmarksView(getActivity());
|
||||
list.addHeaderView(mTopBookmarks);
|
||||
mList.addHeaderView(mTopBookmarks);
|
||||
|
||||
return list;
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -115,7 +131,6 @@ public class BookmarksPage extends HomeFragment {
|
||||
|
||||
mPinBookmarkListener = new PinBookmarkListener();
|
||||
|
||||
mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
|
||||
mList.setTag(HomePager.LIST_TAG_BOOKMARKS);
|
||||
mList.setOnUrlOpenListener(listener);
|
||||
mList.setHeaderDividersEnabled(false);
|
||||
@ -125,6 +140,15 @@ public class BookmarksPage extends HomeFragment {
|
||||
|
||||
registerForContextMenu(mList);
|
||||
registerForContextMenu(mTopBookmarks);
|
||||
|
||||
mBanner = (HomeBanner) view.findViewById(R.id.home_banner);
|
||||
mList.setOnTouchListener(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
BookmarksPage.this.handleListTouchEvent(event);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -190,6 +214,60 @@ public class BookmarksPage extends HomeFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleListTouchEvent(MotionEvent event) {
|
||||
// Ignore the event if the banner is hidden for this session.
|
||||
if (mBanner.isDismissed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
mListTouchY = event.getRawY();
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
// There is a chance that we won't receive ACTION_DOWN, if the touch event
|
||||
// actually started on the Grid instead of the List. Treat this as first event.
|
||||
if (mListTouchY == -1) {
|
||||
mListTouchY = event.getRawY();
|
||||
return;
|
||||
}
|
||||
|
||||
final float curY = event.getRawY();
|
||||
final float delta = mListTouchY - curY;
|
||||
mSnapBannerToTop = (delta > 0.0f) ? false : true;
|
||||
|
||||
final float height = mBanner.getHeight();
|
||||
float newTranslationY = ViewHelper.getTranslationY(mBanner) + delta;
|
||||
|
||||
// Clamp the values to be between 0 and height.
|
||||
if (newTranslationY < 0.0f) {
|
||||
newTranslationY = 0.0f;
|
||||
} else if (newTranslationY > height) {
|
||||
newTranslationY = height;
|
||||
}
|
||||
|
||||
ViewHelper.setTranslationY(mBanner, newTranslationY);
|
||||
mListTouchY = curY;
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
mListTouchY = -1;
|
||||
final float y = ViewHelper.getTranslationY(mBanner);
|
||||
final float height = mBanner.getHeight();
|
||||
if (y > 0.0f && y < height) {
|
||||
final PropertyAnimator animator = new PropertyAnimator(100);
|
||||
animator.attach(mBanner, Property.TRANSLATION_Y, mSnapBannerToTop ? 0 : height);
|
||||
animator.start();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
|
||||
if (menuInfo == null) {
|
||||
|
@ -882,7 +882,7 @@ public class BrowserSearch extends HomeFragment
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
return false;
|
||||
return super.onInterceptTouchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
51
mobile/android/base/home/HomeBanner.java
Normal file
51
mobile/android/base/home/HomeBanner.java
Normal file
@ -0,0 +1,51 @@
|
||||
/* -*- 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.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class HomeBanner extends LinearLayout {
|
||||
|
||||
public HomeBanner(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public HomeBanner(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.home_banner, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
// Tapping on the close button will ensure that the banner is never
|
||||
// showed again on this session.
|
||||
final ImageButton closeButton = (ImageButton) findViewById(R.id.close);
|
||||
|
||||
// The drawable should have 50% opacity.
|
||||
closeButton.getDrawable().setAlpha(127);
|
||||
|
||||
closeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
HomeBanner.this.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isDismissed() {
|
||||
return (getVisibility() == View.GONE);
|
||||
}
|
||||
}
|
@ -99,6 +99,20 @@ public class HomeListView extends ListView
|
||||
return mContextMenuInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnItemClickListener(final AdapterView.OnItemClickListener listener) {
|
||||
super.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (mShowTopDivider) {
|
||||
position--;
|
||||
}
|
||||
|
||||
listener.onItemClick(parent, view, position, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public OnUrlOpenListener getOnUrlOpenListener() {
|
||||
return mUrlOpenListener;
|
||||
}
|
||||
|
38
mobile/android/base/resources/drawable/home_banner.xml
Normal file
38
mobile/android/base/resources/drawable/home_banner.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?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/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true">
|
||||
<layer-list>
|
||||
<item android:left="-2dp"
|
||||
android:right="-2dp"
|
||||
android:bottom="-2dp">
|
||||
|
||||
<shape android:shape="rectangle" >
|
||||
<stroke android:width="2dp"
|
||||
android:color="#FFE0E4E7" />
|
||||
<solid android:color="#FFC5D0DA" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layer-list>
|
||||
<item android:left="-2dp"
|
||||
android:right="-2dp"
|
||||
android:bottom="-2dp">
|
||||
|
||||
<shape android:shape="rectangle" >
|
||||
<stroke android:width="2dp"
|
||||
android:color="#FFE0E4E7" />
|
||||
<solid android:color="@color/background_light" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
|
||||
</selector>
|
36
mobile/android/base/resources/layout/home_banner.xml
Normal file
36
mobile/android/base/resources/layout/home_banner.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<?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/. -->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<ImageView android:id="@+id/icon"
|
||||
android:layout_width="48dip"
|
||||
android:layout_height="48dip"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:scaleType="centerInside"/>
|
||||
|
||||
<TextView android:id="@+id/text"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:textAppearance="@style/TextAppearance.Widget.HomeBanner"
|
||||
android:layout_gravity="bottom"
|
||||
android:singleLine="false"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
<ImageButton android:id="@+id/close"
|
||||
android:layout_width="34dip"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/home_banner"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/close_tab"
|
||||
android:src="@drawable/tab_close"/>
|
||||
|
||||
</merge>
|
@ -3,7 +3,24 @@
|
||||
- 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.BookmarksListView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/bookmarks_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"/>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<org.mozilla.gecko.home.BookmarksListView
|
||||
android:id="@+id/bookmarks_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"/>
|
||||
|
||||
<org.mozilla.gecko.home.HomeBanner android:id="@+id/home_banner"
|
||||
style="@style/Widget.HomeBanner"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/home_banner_height"
|
||||
android:background="@drawable/home_banner"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
@ -50,4 +50,15 @@
|
||||
<item name="android:paddingTop">30dp</item>
|
||||
</style>
|
||||
|
||||
<!--
|
||||
The content of the banner should align with the Grid/List views
|
||||
in BookmarksPage. BookmarksListView has a 120dp padding and
|
||||
the TwoLinePageRows have a 50dp padding. Hence HomeBanner should
|
||||
have 170dp padding.
|
||||
-->
|
||||
<style name="Widget.HomeBanner">
|
||||
<item name="android:paddingLeft">170dp</item>
|
||||
<item name="android:paddingRight">170dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -60,4 +60,9 @@
|
||||
<item name="topDivider">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.HomeBanner">
|
||||
<item name="android:paddingLeft">32dp</item>
|
||||
<item name="android:paddingRight">32dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -87,4 +87,7 @@
|
||||
|
||||
<!-- PageActionButtons dimensions -->
|
||||
<dimen name="page_action_button_width">32dp</dimen>
|
||||
|
||||
<!-- Banner -->
|
||||
<dimen name="home_banner_height">72dp</dimen>
|
||||
</resources>
|
||||
|
@ -160,6 +160,8 @@
|
||||
|
||||
<style name="Widget.ReadingListView" parent="Widget.BookmarksListView"/>
|
||||
|
||||
<style name="Widget.HomeBanner"/>
|
||||
|
||||
<style name="Widget.Home" />
|
||||
|
||||
<style name="Widget.Home.HeaderItem">
|
||||
@ -318,6 +320,10 @@
|
||||
<item name="android:textColor">#00ACFF</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Widget.HomeBanner" parent="TextAppearance.Small">
|
||||
<item name="android:textColor">?android:attr/textColorHint</item>
|
||||
</style>
|
||||
|
||||
<!-- BrowserToolbar -->
|
||||
<style name="BrowserToolbar">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
|
@ -2563,6 +2563,16 @@
|
||||
"n_buckets": 15,
|
||||
"description": "Session restore: Days elapsed since the session was first started"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TABSTATECACHE_HIT_RATE": {
|
||||
"kind": "enumerated",
|
||||
"n_values": 101,
|
||||
"description": "Session restore: Percentage of tab state cache hits in all tab state cache accesses"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TABSTATECACHE_CLEAR_RATIO": {
|
||||
"kind": "enumerated",
|
||||
"n_values": 101,
|
||||
"description": "Session restore: Number of times the tab state cache has been cleared during a session divided by number of total accesses during the session (percentage)"
|
||||
},
|
||||
"INNERWINDOWS_WITH_MUTATION_LISTENERS": {
|
||||
"kind": "boolean",
|
||||
"description": "Deleted or to-be-reused innerwindow which has had mutation event listeners."
|
||||
|
@ -7,6 +7,8 @@
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/PageThumbs.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const backgroundPageThumbsContent = {
|
||||
|
||||
@ -26,9 +28,14 @@ const backgroundPageThumbsContent = {
|
||||
|
||||
docShell.allowMedia = false;
|
||||
docShell.allowPlugins = false;
|
||||
docShell.allowContentRetargeting = false;
|
||||
|
||||
addMessageListener("BackgroundPageThumbs:capture",
|
||||
this._onCapture.bind(this));
|
||||
docShell.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebProgress).
|
||||
addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
|
||||
},
|
||||
|
||||
get _webNav() {
|
||||
@ -36,52 +43,64 @@ const backgroundPageThumbsContent = {
|
||||
},
|
||||
|
||||
_onCapture: function (msg) {
|
||||
this._webNav.stop(Ci.nsIWebNavigation.STOP_NETWORK);
|
||||
if (this._onLoad)
|
||||
removeEventListener("load", this._onLoad, true);
|
||||
|
||||
this._onLoad = function onLoad(event) {
|
||||
if (event.target != content.document)
|
||||
return;
|
||||
let pageLoadTime = new Date() - loadDate;
|
||||
removeEventListener("load", this._onLoad, true);
|
||||
delete this._onLoad;
|
||||
|
||||
let canvas = PageThumbs._createCanvas(content);
|
||||
let captureDate = new Date();
|
||||
PageThumbs._captureToCanvas(content, canvas);
|
||||
let captureTime = new Date() - captureDate;
|
||||
|
||||
let channel = docShell.currentDocumentChannel;
|
||||
let isErrorResponse = PageThumbs._isChannelErrorResponse(channel);
|
||||
let finalURL = this._webNav.currentURI.spec;
|
||||
let fileReader = Cc["@mozilla.org/files/filereader;1"].
|
||||
createInstance(Ci.nsIDOMFileReader);
|
||||
fileReader.onloadend = function onArrayBufferLoad() {
|
||||
sendAsyncMessage("BackgroundPageThumbs:didCapture", {
|
||||
id: msg.json.id,
|
||||
imageData: fileReader.result,
|
||||
finalURL: finalURL,
|
||||
telemetry: {
|
||||
CAPTURE_PAGE_LOAD_TIME_MS: pageLoadTime,
|
||||
CAPTURE_CANVAS_DRAW_TIME_MS: captureTime,
|
||||
},
|
||||
wasErrorResponse: isErrorResponse,
|
||||
});
|
||||
};
|
||||
canvas.toBlob(blob => fileReader.readAsArrayBuffer(blob));
|
||||
|
||||
// Load about:blank to cause the captured window to be collected...
|
||||
// eventually.
|
||||
this._webNav.loadURI("about:blank", Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
||||
null, null, null);
|
||||
}.bind(this);
|
||||
|
||||
addEventListener("load", this._onLoad, true);
|
||||
this._webNav.loadURI(msg.json.url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
||||
this._webNav.loadURI(msg.json.url,
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_STOP_CONTENT,
|
||||
null, null, null);
|
||||
let loadDate = new Date();
|
||||
// If a page was already loading, onStateChange is synchronously called at
|
||||
// this point by loadURI.
|
||||
this._requestID = msg.json.id;
|
||||
this._requestDate = new Date();
|
||||
},
|
||||
|
||||
onStateChange: function (webProgress, req, flags, status) {
|
||||
if (!webProgress.isTopLevel ||
|
||||
!(flags & Ci.nsIWebProgressListener.STATE_STOP) ||
|
||||
req.name == "about:blank")
|
||||
return;
|
||||
|
||||
let requestID = this._requestID;
|
||||
let pageLoadTime = new Date() - this._requestDate;
|
||||
delete this._requestID;
|
||||
|
||||
let canvas = PageThumbs._createCanvas(content);
|
||||
let captureDate = new Date();
|
||||
PageThumbs._captureToCanvas(content, canvas);
|
||||
let captureTime = new Date() - captureDate;
|
||||
|
||||
let channel = docShell.currentDocumentChannel;
|
||||
let isErrorResponse = PageThumbs._isChannelErrorResponse(channel);
|
||||
let finalURL = this._webNav.currentURI.spec;
|
||||
let fileReader = Cc["@mozilla.org/files/filereader;1"].
|
||||
createInstance(Ci.nsIDOMFileReader);
|
||||
fileReader.onloadend = () => {
|
||||
sendAsyncMessage("BackgroundPageThumbs:didCapture", {
|
||||
id: requestID,
|
||||
imageData: fileReader.result,
|
||||
finalURL: finalURL,
|
||||
wasErrorResponse: isErrorResponse,
|
||||
telemetry: {
|
||||
CAPTURE_PAGE_LOAD_TIME_MS: pageLoadTime,
|
||||
CAPTURE_CANVAS_DRAW_TIME_MS: captureTime,
|
||||
},
|
||||
});
|
||||
};
|
||||
canvas.toBlob(blob => fileReader.readAsArrayBuffer(blob));
|
||||
|
||||
// If no other pages are loading, load about:blank to cause the captured
|
||||
// window to be collected... eventually. Calling loadURI at this point
|
||||
// trips an assertion in nsLoadGroup::Cancel, so do it on another stack.
|
||||
Services.tm.mainThread.dispatch(() => {
|
||||
if (!("_requestID" in this))
|
||||
this._webNav.loadURI("about:blank",
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_STOP_CONTENT,
|
||||
null, null, null);
|
||||
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
]),
|
||||
};
|
||||
|
||||
backgroundPageThumbsContent.init();
|
||||
|
@ -54,7 +54,7 @@ let tests = [
|
||||
let files = urls.map(fileForURL);
|
||||
files.forEach(f => ok(!f.exists(), "Thumbnail should not be cached yet."));
|
||||
urls.forEach(function (url) {
|
||||
let isTimeoutTest = url.indexOf("?wait") >= 0;
|
||||
let isTimeoutTest = url.indexOf("wait") >= 0;
|
||||
imports.BackgroundPageThumbs.capture(url, {
|
||||
timeout: isTimeoutTest ? 100 : 30000,
|
||||
onDone: function onDone(capturedURL) {
|
||||
|
Loading…
Reference in New Issue
Block a user