mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
merge mozilla-central to autoland. r=merge a=merge
This commit is contained in:
commit
7fccfb74e9
@ -8,8 +8,25 @@ module.exports = {
|
||||
"rules": {
|
||||
// XXX Bug 1326071 - This should be reduced down - probably to 20 or to
|
||||
// be removed & synced with the mozilla/recommended value.
|
||||
"complexity": ["error", {"max": 42}],
|
||||
"complexity": ["error", {"max": 40}],
|
||||
|
||||
// Disallow empty statements. This will report an error for:
|
||||
// try { something(); } catch (e) {}
|
||||
// but will not report it for:
|
||||
// try { something(); } catch (e) { /* Silencing the error because ...*/ }
|
||||
// which is a valid use case.
|
||||
"no-empty": "error",
|
||||
|
||||
// No spaces between function name and parentheses
|
||||
"no-spaced-func": "error",
|
||||
|
||||
// Maximum depth callbacks can be nested.
|
||||
"max-nested-callbacks": ["error", 8],
|
||||
|
||||
// Disallow adding to native types
|
||||
"no-extend-native": "error",
|
||||
|
||||
"no-mixed-spaces-and-tabs": "error",
|
||||
"no-shadow": "error",
|
||||
}
|
||||
};
|
||||
|
@ -112,7 +112,7 @@ this.tabs = class extends ExtensionAPI {
|
||||
|
||||
onCreated: new SingletonEventManager(context, "tabs.onCreated", fire => {
|
||||
let listener = (eventName, event) => {
|
||||
fire.async(tabManager.convert(event.nativeTab));
|
||||
fire.async(tabManager.convert(event.nativeTab, event.currentTab));
|
||||
};
|
||||
|
||||
tabTracker.on("tab-created", listener);
|
||||
@ -360,6 +360,7 @@ this.tabs = class extends ExtensionAPI {
|
||||
options.disallowInheritPrincipal = true;
|
||||
|
||||
tabListener.initTabReady();
|
||||
let currentTab = window.gBrowser.selectedTab;
|
||||
let nativeTab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
|
||||
|
||||
let active = true;
|
||||
@ -394,7 +395,7 @@ this.tabs = class extends ExtensionAPI {
|
||||
tabListener.initializingTabs.add(nativeTab);
|
||||
}
|
||||
|
||||
return tabManager.convert(nativeTab);
|
||||
return tabManager.convert(nativeTab, currentTab);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -238,13 +238,18 @@ class TabTracker extends TabTrackerBase {
|
||||
});
|
||||
}
|
||||
|
||||
// Save the current tab, since the newly-created tab will likely be
|
||||
// active by the time the promise below resolves and the event is
|
||||
// dispatched.
|
||||
let currentTab = nativeTab.ownerGlobal.gBrowser.selectedTab;
|
||||
|
||||
// We need to delay sending this event until the next tick, since the
|
||||
// tab does not have its final index when the TabOpen event is dispatched.
|
||||
Promise.resolve().then(() => {
|
||||
if (event.detail.adoptedTab) {
|
||||
this.emitAttached(event.originalTarget);
|
||||
} else {
|
||||
this.emitCreated(event.originalTarget);
|
||||
this.emitCreated(event.originalTarget, currentTab);
|
||||
}
|
||||
});
|
||||
break;
|
||||
@ -389,10 +394,12 @@ class TabTracker extends TabTrackerBase {
|
||||
*
|
||||
* @param {NativeTab} nativeTab
|
||||
* The tab element which is being created.
|
||||
* @param {NativeTab} [currentTab]
|
||||
* The tab element for the currently active tab.
|
||||
* @private
|
||||
*/
|
||||
emitCreated(nativeTab) {
|
||||
this.emit("tab-created", {nativeTab});
|
||||
emitCreated(nativeTab, currentTab) {
|
||||
this.emit("tab-created", {nativeTab, currentTab});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -479,12 +486,18 @@ class Tab extends TabBase {
|
||||
return this.nativeTab.linkedBrowser;
|
||||
}
|
||||
|
||||
get frameLoader() {
|
||||
// If we don't have a frameLoader yet, just return a dummy with no width and
|
||||
// height.
|
||||
return super.frameLoader || {lazyWidth: 0, lazyHeight: 0};
|
||||
}
|
||||
|
||||
get cookieStoreId() {
|
||||
return getCookieStoreIdForTab(this, this.nativeTab);
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.browser.clientHeight;
|
||||
return this.frameLoader.lazyHeight;
|
||||
}
|
||||
|
||||
get index() {
|
||||
@ -525,7 +538,7 @@ class Tab extends TabBase {
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this.browser.clientWidth;
|
||||
return this.frameLoader.lazyWidth;
|
||||
}
|
||||
|
||||
get window() {
|
||||
|
@ -19,7 +19,6 @@ module.exports = {
|
||||
"no-eval": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-fallthrough": ["error", { "commentPattern": ".*[Ii]ntentional(?:ly)?\\s+fall(?:ing)?[\\s-]*through.*" }],
|
||||
"no-mixed-spaces-and-tabs": "error",
|
||||
"no-multi-str": "error",
|
||||
"no-return-assign": "error",
|
||||
"no-sequences": "error",
|
||||
|
@ -511,6 +511,8 @@ PlacesViewBase.prototype = {
|
||||
if (elt.localName == "menupopup") {
|
||||
elt = elt.parentNode;
|
||||
}
|
||||
// We must remove and reset the attribute to force an update.
|
||||
elt.removeAttribute("image");
|
||||
elt.setAttribute("image", aPlacesNode.icon);
|
||||
},
|
||||
|
||||
|
@ -781,8 +781,11 @@ PlacesTreeView.prototype = {
|
||||
return;
|
||||
|
||||
let column = this._findColumnByType(aColumnType);
|
||||
if (column && !column.element.hidden)
|
||||
if (column && !column.element.hidden) {
|
||||
if (aColumnType == this.COLUMN_TYPE_TITLE)
|
||||
this._tree.removeImageCacheEntry(row, column);
|
||||
this._tree.invalidateCell(row, column);
|
||||
}
|
||||
|
||||
// Last modified time is altered for almost all node changes.
|
||||
if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
|
||||
|
@ -53,6 +53,9 @@ support-files =
|
||||
skip-if = true # temporarily disabled for breaking the treeview - bug 658744
|
||||
[browser_sort_in_library.js]
|
||||
[browser_toolbarbutton_menu_context.js]
|
||||
[browser_views_iconsupdate.js]
|
||||
support-files =
|
||||
favicon-normal16.png
|
||||
[browser_views_liveupdate.js]
|
||||
[browser_bookmark_all_tabs.js]
|
||||
support-files =
|
||||
|
@ -0,0 +1,119 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests Places views (toolbar, tree) for icons update.
|
||||
* The menu is not tested since it uses the same code as the toolbar.
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
const PAGE_URI = NetUtil.newURI("http://places.test/");
|
||||
const ICON_URI = NetUtil.newURI("http://mochi.test:8888/browser/browser/components/places/tests/browser/favicon-normal16.png");
|
||||
|
||||
info("Uncollapse the personal toolbar if needed");
|
||||
let toolbar = document.getElementById("PersonalToolbar");
|
||||
let wasCollapsed = toolbar.collapsed;
|
||||
if (wasCollapsed) {
|
||||
yield promiseSetToolbarVisibility(toolbar, true);
|
||||
registerCleanupFunction(function* () {
|
||||
yield promiseSetToolbarVisibility(toolbar, false);
|
||||
});
|
||||
}
|
||||
|
||||
info("Open the bookmarks sidebar");
|
||||
let sidebar = document.getElementById("sidebar");
|
||||
let promiseSidebarLoaded = new Promise(resolve => {
|
||||
sidebar.addEventListener("load", resolve, {capture: true, once: true});
|
||||
});
|
||||
SidebarUI.show("viewBookmarksSidebar");
|
||||
registerCleanupFunction(() => {
|
||||
SidebarUI.hide();
|
||||
});
|
||||
yield promiseSidebarLoaded;
|
||||
|
||||
// Add a bookmark to the bookmarks toolbar.
|
||||
let bm = yield PlacesUtils.bookmarks.insert({
|
||||
url: PAGE_URI,
|
||||
title: "test icon",
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid
|
||||
});
|
||||
registerCleanupFunction(function* () {
|
||||
yield PlacesUtils.bookmarks.remove(bm);
|
||||
});
|
||||
|
||||
// The icon is read asynchronously from the network, we don't have an easy way
|
||||
// to wait for that.
|
||||
yield new Promise(resolve => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
|
||||
let toolbarElt = getNodeForToolbarItem(bm.guid);
|
||||
let toolbarShot1 = TestUtils.screenshotArea(toolbarElt, window);
|
||||
let sidebarRect = yield getRectForSidebarItem(bm.guid);
|
||||
let sidebarShot1 = TestUtils.screenshotArea(sidebarRect, window);
|
||||
|
||||
yield new Promise(resolve => {
|
||||
PlacesUtils.favicons.setAndFetchFaviconForPage(
|
||||
PAGE_URI, ICON_URI, true,
|
||||
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
|
||||
resolve,
|
||||
Services.scriptSecurityManager.getSystemPrincipal()
|
||||
);
|
||||
});
|
||||
|
||||
// The icon is read asynchronously from the network, we don't have an easy way
|
||||
// to wait for that.
|
||||
yield new Promise(resolve => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
|
||||
// Assert.notEqual truncates the strings, so it is unusable here for failure
|
||||
// debugging purposes.
|
||||
let toolbarShot2 = TestUtils.screenshotArea(toolbarElt, window);
|
||||
if (toolbarShot1 != toolbarShot2) {
|
||||
info("Before toolbar: " + toolbarShot1);
|
||||
info("After toolbar: " + toolbarShot2);
|
||||
}
|
||||
Assert.notEqual(toolbarShot1, toolbarShot2, "The UI should have updated");
|
||||
|
||||
let sidebarShot2 = TestUtils.screenshotArea(sidebarRect, window);
|
||||
if (sidebarShot1 != sidebarShot2) {
|
||||
info("Before sidebar: " + sidebarShot1);
|
||||
info("After sidebar: " + sidebarShot2);
|
||||
}
|
||||
Assert.notEqual(sidebarShot1, sidebarShot2, "The UI should have updated");
|
||||
});
|
||||
|
||||
/**
|
||||
* Get Element for a bookmark in the bookmarks toolbar.
|
||||
*
|
||||
* @param guid
|
||||
* GUID of the item to search.
|
||||
* @returns DOM Node of the element.
|
||||
*/
|
||||
function getNodeForToolbarItem(guid) {
|
||||
return Array.from(document.getElementById("PlacesToolbarItems").childNodes)
|
||||
.find(child => child._placesNode && child._placesNode.bookmarkGuid == guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a rect for a bookmark in the bookmarks sidebar
|
||||
*
|
||||
* @param guid
|
||||
* GUID of the item to search.
|
||||
* @returns DOM Node of the element.
|
||||
*/
|
||||
function* getRectForSidebarItem(guid) {
|
||||
let itemId = yield PlacesUtils.promiseItemId(guid);
|
||||
let sidebar = document.getElementById("sidebar");
|
||||
let tree = sidebar.contentDocument.getElementById("bookmarks-view");
|
||||
tree.selectItems([itemId]);
|
||||
let rect = {};
|
||||
[rect.left, rect.top, rect.width, rect.height] = tree.treeBoxObject
|
||||
.selectionRegion
|
||||
.getRects();
|
||||
// Adjust the position for the sidebar.
|
||||
rect.left += sidebar.getBoundingClientRect().left;
|
||||
rect.top += sidebar.getBoundingClientRect().top;
|
||||
return rect;
|
||||
}
|
BIN
browser/components/places/tests/browser/favicon-normal16.png
Normal file
BIN
browser/components/places/tests/browser/favicon-normal16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 286 B |
@ -4,6 +4,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TestUtils",
|
||||
"resource://testing-common/TestUtils.jsm");
|
||||
|
||||
// Imported via PlacesOverlay.xul
|
||||
/* global doGetPlacesControllerForCommand:false, PlacesControllerDragHelper:false,
|
||||
|
@ -1076,22 +1076,23 @@
|
||||
|
||||
<handlers>
|
||||
<handler event="popupshowing"><![CDATA[
|
||||
if (!this._computedMinWidth) {
|
||||
// The panel width only spans to the textbox size, but we also want it
|
||||
// to include the magnifier icon's width.
|
||||
let ltr = getComputedStyle(this).direction == "ltr";
|
||||
let magnifierWidth = parseInt(getComputedStyle(this)[
|
||||
ltr ? "marginLeft" : "marginRight"
|
||||
]) * -1;
|
||||
// Ensure the panel is wide enough to fit at least 3 engines.
|
||||
let minWidth = Math.max(
|
||||
parseInt(this.width) + magnifierWidth,
|
||||
this.oneOffButtons.buttonWidth * 3
|
||||
);
|
||||
this.style.minWidth = minWidth + "px";
|
||||
// Force the panel to have the width of the searchbar rather than
|
||||
// the width of the textfield.
|
||||
let DOMUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let textboxRect = DOMUtils.getBoundsWithoutFlushing(this.mInput);
|
||||
let inputRect = DOMUtils.getBoundsWithoutFlushing(this.mInput.inputField);
|
||||
|
||||
this._computedMinWidth = true;
|
||||
}
|
||||
// Ensure the panel is wide enough to fit at least 3 engines.
|
||||
let minWidth = Math.max(textboxRect.width,
|
||||
this.oneOffButtons.buttonWidth * 3);
|
||||
this.style.minWidth = minWidth + "px";
|
||||
// Alignment of the panel with the searchbar is obtained with negative
|
||||
// margins.
|
||||
this.style.marginLeft = (textboxRect.left - inputRect.left) + "px";
|
||||
// This second margin is needed when the direction is reversed,
|
||||
// eg. when using command+shift+X.
|
||||
this.style.marginRight = (inputRect.right - textboxRect.right) + "px";
|
||||
|
||||
// First handle deciding if we are showing the reduced version of the
|
||||
// popup containing only the preferences button. We do this if the
|
||||
|
@ -12,7 +12,7 @@ module.exports = {
|
||||
"rules": {
|
||||
// Rules from the mozilla plugin
|
||||
"mozilla/balanced-listeners": "error",
|
||||
"mozilla/no-aArgs": "warn",
|
||||
"mozilla/no-aArgs": "error",
|
||||
"mozilla/no-cpows-in-tests": "error",
|
||||
"mozilla/var-only-at-top-level": "error",
|
||||
|
||||
@ -58,16 +58,6 @@ module.exports = {
|
||||
// Use [] instead of Array()
|
||||
"no-array-constructor": "error",
|
||||
|
||||
// Disallow empty statements. This will report an error for:
|
||||
// try { something(); } catch (e) {}
|
||||
// but will not report it for:
|
||||
// try { something(); } catch (e) { /* Silencing the error because ...*/ }
|
||||
// which is a valid use case.
|
||||
"no-empty": "error",
|
||||
|
||||
// No spaces between function name and parentheses
|
||||
"no-spaced-func": "warn",
|
||||
|
||||
// No expressions where a statement is expected
|
||||
"no-unused-expressions": "error",
|
||||
|
||||
@ -117,9 +107,6 @@ module.exports = {
|
||||
// Disallow use of eval(). We have other APIs to evaluate code in content.
|
||||
"no-eval": "error",
|
||||
|
||||
// Disallow adding to native types
|
||||
"no-extend-native": "error",
|
||||
|
||||
// Disallow fallthrough of case statements, except if there is a comment.
|
||||
"no-fallthrough": "error",
|
||||
|
||||
@ -164,9 +151,6 @@ module.exports = {
|
||||
// Disallow function or variable declarations in nested blocks
|
||||
"no-inner-declarations": "error",
|
||||
|
||||
// Disallow labels that share a name with a variable
|
||||
"no-label-var": "error",
|
||||
|
||||
// Disallow creating new instances of String, Number, and Boolean
|
||||
"no-new-wrappers": "error",
|
||||
},
|
||||
|
@ -49,12 +49,12 @@ var FormAutofillFrameScript = {
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage(aMessage) {
|
||||
receiveMessage(message) {
|
||||
if (!Services.prefs.getBoolPref("browser.formautofill.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
switch (message.name) {
|
||||
case "FormAutofill:PreviewProfile":
|
||||
case "FormAutoComplete:PopupClosed":
|
||||
FormAutofillContent._previewProfile(content.document);
|
||||
|
@ -2,17 +2,6 @@
|
||||
* 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/. */
|
||||
|
||||
#PopupSearchAutoComplete {
|
||||
/* JS code forces the panel to have the width of the searchbar rather than
|
||||
* the width of the textfield. Alignment of the panel with the searchbar is
|
||||
* obtained with negative margins here: margin-inline-start when the text
|
||||
* field is in the same direction as the rest of the UI, margin-inline-end
|
||||
* when the textfield's direction has been reversed.
|
||||
* (eg. using ctrl+shift+X) */
|
||||
margin-inline-start: -23px;
|
||||
margin-inline-end: -16px;
|
||||
}
|
||||
|
||||
.autocomplete-textbox-container {
|
||||
-moz-box-align: stretch;
|
||||
}
|
||||
|
@ -2,17 +2,6 @@
|
||||
* 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/. */
|
||||
|
||||
#PopupSearchAutoComplete {
|
||||
/* JS code forces the panel to have the width of the searchbar rather than
|
||||
* the width of the textfield. Alignment of the panel with the searchbar is
|
||||
* obtained with negative margins here: margin-inline-start when the text
|
||||
* field is in the same direction as the rest of the UI, margin-inline-end
|
||||
* when the textfield's direction has been reversed.
|
||||
* (eg. using command+shift+X) */
|
||||
margin-inline-start: -23px;
|
||||
margin-inline-end: -21px;
|
||||
}
|
||||
|
||||
.searchbar-textbox {
|
||||
border-radius: 10000px;
|
||||
}
|
||||
|
@ -2,17 +2,6 @@
|
||||
* 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/. */
|
||||
|
||||
#PopupSearchAutoComplete {
|
||||
/* JS code forces the panel to have the width of the searchbar rather than
|
||||
* the width of the textfield. Alignment of the panel with the searchbar is
|
||||
* obtained with negative margins here: margin-inline-start when the text
|
||||
* field is in the same direction as the rest of the UI, margin-inline-end
|
||||
* when the textfield's direction has been reversed.
|
||||
* (eg. using ctrl+shift+X) */
|
||||
margin-inline-start: -25px;
|
||||
margin-inline-end: -18px;
|
||||
}
|
||||
|
||||
.autocomplete-textbox-container {
|
||||
-moz-box-align: stretch;
|
||||
}
|
||||
|
@ -914,7 +914,11 @@ cargo_build_flags += --frozen
|
||||
cargo_build_flags += --manifest-path $(CARGO_FILE)
|
||||
ifdef BUILD_VERBOSE_LOG
|
||||
cargo_build_flags += --verbose
|
||||
endif
|
||||
else
|
||||
ifdef MOZ_AUTOMATION
|
||||
cargo_build_flags += --verbose
|
||||
endif # MOZ_AUTOMATION
|
||||
endif # BUILD_VERBOSE_LOG
|
||||
|
||||
# Enable color output if original stdout was a TTY and color settings
|
||||
# aren't already present. This essentially restores the default behavior
|
||||
|
@ -204,4 +204,10 @@ interface TreeBoxObject : BoxObject {
|
||||
* Called on a theme switch to flush out the tree's style and image caches.
|
||||
*/
|
||||
void clearStyleAndImageCaches();
|
||||
|
||||
/**
|
||||
* Remove an image source from the image cache to allow its invalidation.
|
||||
*/
|
||||
[Throws]
|
||||
void removeImageCacheEntry(long row, TreeColumn col);
|
||||
};
|
||||
|
@ -10,6 +10,8 @@ using CSSToScreenScale from "Units.h";
|
||||
using ScreenIntSize from "Units.h";
|
||||
using ScreenPoint from "Units.h";
|
||||
|
||||
include "mozilla/GfxMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
|
@ -296,6 +296,17 @@ nsPluginFrame::PrepForDrawing(nsIWidget *aWidget)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// We can already have mInnerView if our instance owner went away and then
|
||||
// came back. So clear the old one before creating a new one.
|
||||
if (mInnerView) {
|
||||
if (mInnerView->GetWidget()) {
|
||||
// The widget listener should have already been cleared by
|
||||
// SetInstanceOwner (with a null instance owner).
|
||||
MOZ_RELEASE_ASSERT(mInnerView->GetWidget()->GetWidgetListener() == nullptr);
|
||||
}
|
||||
mInnerView->Destroy();
|
||||
mInnerView = nullptr;
|
||||
}
|
||||
mInnerView = viewMan->CreateView(GetContentRectRelativeToSelf(), view);
|
||||
if (!mInnerView) {
|
||||
NS_ERROR("Could not create inner view");
|
||||
|
@ -667,6 +667,24 @@ TreeBoxObject::ClearStyleAndImageCaches()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TreeBoxObject::RemoveImageCacheEntry(int32_t aRowIndex, nsITreeColumn* aCol)
|
||||
{
|
||||
NS_ENSURE_ARG(aCol);
|
||||
NS_ENSURE_TRUE(aRowIndex >= 0, NS_ERROR_INVALID_ARG);
|
||||
nsTreeBodyFrame* body = GetTreeBodyFrame();
|
||||
if (body) {
|
||||
return body->RemoveImageCacheEntry(aRowIndex, aCol);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TreeBoxObject::RemoveImageCacheEntry(int32_t row, nsITreeColumn& col, ErrorResult& aRv)
|
||||
{
|
||||
aRv = RemoveImageCacheEntry(row, &col);
|
||||
}
|
||||
|
||||
void
|
||||
TreeBoxObject::ClearCachedValues()
|
||||
{
|
||||
|
@ -75,6 +75,8 @@ public:
|
||||
|
||||
bool IsCellCropped(int32_t row, nsITreeColumn* col, ErrorResult& aRv);
|
||||
|
||||
void RemoveImageCacheEntry(int32_t row, nsITreeColumn& col, ErrorResult& aRv);
|
||||
|
||||
// Deprecated APIs from old IDL
|
||||
void GetCellAt(JSContext* cx,
|
||||
int32_t x, int32_t y,
|
||||
|
@ -48,7 +48,7 @@ interface nsITreeBoxObject : nsISupports
|
||||
readonly attribute long rowWidth;
|
||||
|
||||
/**
|
||||
* Get the pixel position of the horizontal scrollbar.
|
||||
* Get the pixel position of the horizontal scrollbar.
|
||||
*/
|
||||
readonly attribute long horizontalPosition;
|
||||
|
||||
@ -186,4 +186,12 @@ interface nsITreeBoxObject : nsISupports
|
||||
* Called on a theme switch to flush out the tree's style and image caches.
|
||||
*/
|
||||
void clearStyleAndImageCaches();
|
||||
|
||||
/**
|
||||
* Remove an image source from the image cache to allow its invalidation.
|
||||
*
|
||||
* @note This only affects images supplied by the view, not the ones supplied
|
||||
* through the styling context, like twisties or checkboxes.
|
||||
*/
|
||||
void removeImageCacheEntry(in long row, in nsITreeColumn col);
|
||||
};
|
||||
|
@ -1109,7 +1109,7 @@ nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const n
|
||||
bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
|
||||
nscoord currX = mInnerBox.x - mHorzPosition;
|
||||
|
||||
// The Rect for the requested item.
|
||||
// The Rect for the requested item.
|
||||
nsRect theRect;
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
@ -4529,6 +4529,22 @@ nsTreeBodyFrame::ClearStyleAndImageCaches()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTreeBodyFrame::RemoveImageCacheEntry(int32_t aRowIndex, nsITreeColumn* aCol)
|
||||
{
|
||||
nsAutoString imageSrc;
|
||||
if (NS_SUCCEEDED(mView->GetImageSrc(aRowIndex, aCol, imageSrc))) {
|
||||
nsTreeImageCacheEntry entry;
|
||||
if (mImageCache.Get(imageSrc, &entry)) {
|
||||
nsLayoutUtils::DeregisterImageRequest(PresContext(), entry.request,
|
||||
nullptr);
|
||||
entry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
|
||||
mImageCache.Remove(imageSrc);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
||||
{
|
||||
|
@ -118,6 +118,7 @@ public:
|
||||
nsresult BeginUpdateBatch();
|
||||
nsresult EndUpdateBatch();
|
||||
nsresult ClearStyleAndImageCaches();
|
||||
nsresult RemoveImageCacheEntry(int32_t aRowIndex, nsITreeColumn* aCol);
|
||||
|
||||
void CancelImageRequests();
|
||||
|
||||
|
@ -18,6 +18,7 @@ public class BackButton extends NavButton {
|
||||
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
||||
|
||||
mPath.reset();
|
||||
mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
|
||||
mPath.addCircle(width / 2, height / 2, width / 2, Path.Direction.CW);
|
||||
|
||||
mBorderPath.reset();
|
||||
|
@ -260,18 +260,26 @@ public class GeckoView extends LayerView
|
||||
init(context, newSettings);
|
||||
}
|
||||
|
||||
private void init(Context context, final GeckoViewSettings settings) {
|
||||
private void init(final Context context, final GeckoViewSettings settings) {
|
||||
if (GeckoAppShell.getApplicationContext() == null) {
|
||||
GeckoAppShell.setApplicationContext(context.getApplicationContext());
|
||||
}
|
||||
|
||||
// Set the GeckoInterface if the context is an activity and the GeckoInterface
|
||||
// has not already been set
|
||||
// Set the GeckoInterface if the context is an activity and the
|
||||
// GeckoInterface has not already been set
|
||||
if (context instanceof Activity && getGeckoInterface() == null) {
|
||||
setGeckoInterface(new BaseGeckoInterface(context));
|
||||
GeckoAppShell.setContextGetter(this);
|
||||
}
|
||||
|
||||
final GeckoProfile profile = GeckoProfile.get(
|
||||
context.getApplicationContext());
|
||||
if (GeckoThread.initMainProcess(profile,
|
||||
/* args */ null,
|
||||
/* debugging */ false)) {
|
||||
GeckoThread.launch();
|
||||
}
|
||||
|
||||
// Perform common initialization for Fennec/GeckoView.
|
||||
GeckoAppShell.setLayerView(this);
|
||||
|
||||
|
@ -11,13 +11,8 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import org.mozilla.gecko.BaseGeckoInterface;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.GeckoView;
|
||||
|
||||
import static org.mozilla.gecko.GeckoView.setGeckoInterface;
|
||||
|
||||
public class GeckoViewActivity extends Activity {
|
||||
private static final String LOGTAG = "GeckoViewActivity";
|
||||
private static final String DEFAULT_URL = "https://mozilla.org";
|
||||
@ -30,8 +25,6 @@ public class GeckoViewActivity extends Activity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setGeckoInterface(new BaseGeckoInterface(this));
|
||||
|
||||
setContentView(R.layout.geckoview_activity);
|
||||
|
||||
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
|
||||
@ -42,11 +35,6 @@ public class GeckoViewActivity extends Activity {
|
||||
prompt.filePickerRequestCode = REQUEST_FILE_PICKER;
|
||||
mGeckoView.setPromptDelegate(prompt);
|
||||
|
||||
final GeckoProfile profile = GeckoProfile.get(this);
|
||||
|
||||
GeckoThread.initMainProcess(profile, /* args */ null, /* debugging */ false);
|
||||
GeckoThread.launch();
|
||||
|
||||
loadFromIntent(getIntent());
|
||||
}
|
||||
|
||||
|
@ -61,4 +61,27 @@ this.TestUtils = {
|
||||
}, topic);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes a screenshot of an area and returns it as a data URL.
|
||||
*
|
||||
* @param eltOrRect
|
||||
* The DOM node or rect ({left, top, width, height}) to screenshot.
|
||||
* @param win
|
||||
* The current window.
|
||||
*/
|
||||
screenshotArea(eltOrRect, win) {
|
||||
if (eltOrRect instanceof Ci.nsIDOMElement) {
|
||||
eltOrRect = eltOrRect.getBoundingClientRect();
|
||||
}
|
||||
let { left, top, width, height } = eltOrRect;
|
||||
let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
let ctx = canvas.getContext("2d");
|
||||
let ratio = win.devicePixelRatio;
|
||||
canvas.width = width * ratio;
|
||||
canvas.height = height * ratio;
|
||||
ctx.scale(ratio, ratio);
|
||||
ctx.drawWindow(win, left, top, width, height, "#fff");
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
};
|
||||
|
@ -284,6 +284,15 @@ class TabBase {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {nsIFrameLoader} browser
|
||||
* Returns the frameloader for the given tab.
|
||||
* @readonly
|
||||
*/
|
||||
get frameLoader() {
|
||||
return this.browser.frameLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {string} cookieStoreId
|
||||
* Returns the cookie store identifier for the given tab.
|
||||
@ -454,9 +463,12 @@ class TabBase {
|
||||
* of its properties which the extension is permitted to access, in the format
|
||||
* requried to be returned by WebExtension APIs.
|
||||
*
|
||||
* @param {Tab} [fallbackTab]
|
||||
* A tab to retrieve geometry data from if the lazy geometry data for
|
||||
* this tab hasn't been initialized yet.
|
||||
* @returns {object}
|
||||
*/
|
||||
convert() {
|
||||
convert(fallbackTab = null) {
|
||||
let result = {
|
||||
id: this.id,
|
||||
index: this.index,
|
||||
@ -472,6 +484,13 @@ class TabBase {
|
||||
mutedInfo: this.mutedInfo,
|
||||
};
|
||||
|
||||
// If the tab has not been fully layed-out yet, fallback to the geometry
|
||||
// from a different tab (usually the currently active tab).
|
||||
if (fallbackTab && (!result.width || !result.height)) {
|
||||
result.width = fallbackTab.width;
|
||||
result.height = fallbackTab.height;
|
||||
}
|
||||
|
||||
if (this.extension.hasPermission("cookies")) {
|
||||
result.cookieStoreId = this.cookieStoreId;
|
||||
}
|
||||
@ -1632,11 +1651,15 @@ class TabManagerBase {
|
||||
*
|
||||
* @param {NativeTab} nativeTab
|
||||
* The native tab to convert.
|
||||
* @param {NativeTab} [fallbackTab]
|
||||
* A tab to retrieve geometry data from if the lazy geometry data for
|
||||
* this tab hasn't been initialized yet.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
convert(nativeTab) {
|
||||
return this.getWrapper(nativeTab).convert();
|
||||
convert(nativeTab, fallbackTab = null) {
|
||||
return this.getWrapper(nativeTab)
|
||||
.convert(fallbackTab && this.getWrapper(fallbackTab));
|
||||
}
|
||||
|
||||
// The JSDoc validator does not support @returns tags in abstract functions or
|
||||
|
@ -140,21 +140,19 @@ const URLTYPE_OPENSEARCH = "application/opensearchdescription+xml";
|
||||
const BROWSER_SEARCH_PREF = "browser.search.";
|
||||
const LOCALE_PREF = "general.useragent.locale";
|
||||
|
||||
const USER_DEFINED = "{searchTerms}";
|
||||
const USER_DEFINED = "searchTerms";
|
||||
|
||||
// Custom search parameters
|
||||
const MOZ_OFFICIAL = AppConstants.MOZ_OFFICIAL_BRANDING ? "official" : "unofficial";
|
||||
|
||||
const MOZ_PARAM_LOCALE = /\{moz:locale\}/g;
|
||||
const MOZ_PARAM_DIST_ID = /\{moz:distributionID\}/g;
|
||||
const MOZ_PARAM_OFFICIAL = /\{moz:official\}/g;
|
||||
const MOZ_PARAM_LOCALE = "moz:locale";
|
||||
const MOZ_PARAM_DIST_ID = "moz:distributionID"
|
||||
const MOZ_PARAM_OFFICIAL = "moz:official";
|
||||
|
||||
// Supported OpenSearch parameters
|
||||
// See http://opensearch.a9.com/spec/1.1/querysyntax/#core
|
||||
const OS_PARAM_USER_DEFINED = /\{searchTerms\??\}/g;
|
||||
const OS_PARAM_INPUT_ENCODING = /\{inputEncoding\??\}/g;
|
||||
const OS_PARAM_LANGUAGE = /\{language\??\}/g;
|
||||
const OS_PARAM_OUTPUT_ENCODING = /\{outputEncoding\??\}/g;
|
||||
const OS_PARAM_USER_DEFINED = "searchTerms";
|
||||
const OS_PARAM_INPUT_ENCODING = "inputEncoding";
|
||||
const OS_PARAM_LANGUAGE = "language";
|
||||
const OS_PARAM_OUTPUT_ENCODING = "outputEncoding";
|
||||
|
||||
// Default values
|
||||
const OS_PARAM_LANGUAGE_DEF = "*";
|
||||
@ -164,18 +162,15 @@ const OS_PARAM_INPUT_ENCODING_DEF = "UTF-8";
|
||||
// "Unsupported" OpenSearch parameters. For example, we don't support
|
||||
// page-based results, so if the engine requires that we send the "page index"
|
||||
// parameter, we'll always send "1".
|
||||
const OS_PARAM_COUNT = /\{count\??\}/g;
|
||||
const OS_PARAM_START_INDEX = /\{startIndex\??\}/g;
|
||||
const OS_PARAM_START_PAGE = /\{startPage\??\}/g;
|
||||
const OS_PARAM_COUNT = "count";
|
||||
const OS_PARAM_START_INDEX = "startIndex";
|
||||
const OS_PARAM_START_PAGE = "startPage";
|
||||
|
||||
// Default values
|
||||
const OS_PARAM_COUNT_DEF = "20"; // 20 results
|
||||
const OS_PARAM_START_INDEX_DEF = "1"; // start at 1st result
|
||||
const OS_PARAM_START_PAGE_DEF = "1"; // 1st page
|
||||
|
||||
// Optional parameter
|
||||
const OS_PARAM_OPTIONAL = /\{(?:\w+:)?\w+\?\}/g;
|
||||
|
||||
// A array of arrays containing parameters that we don't fully support, and
|
||||
// their default values. We will only send values for these parameters if
|
||||
// required, since our values are just really arbitrary "guesses" that should
|
||||
@ -989,44 +984,53 @@ function QueryParameter(aName, aValue, aPurpose) {
|
||||
* @see http://opensearch.a9.com/spec/1.1/querysyntax/#core
|
||||
*/
|
||||
function ParamSubstitution(aParamValue, aSearchTerms, aEngine) {
|
||||
var value = aParamValue;
|
||||
const PARAM_REGEXP = /\{((?:\w+:)?\w+)(\??)\}/g;
|
||||
return aParamValue.replace(PARAM_REGEXP, function(match, name, optional) {
|
||||
// {searchTerms} is by far the most common param so handle it first.
|
||||
if (name == USER_DEFINED)
|
||||
return aSearchTerms;
|
||||
|
||||
var distributionID =
|
||||
Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "distributionID",
|
||||
Services.appinfo.distributionID || "");
|
||||
// {inputEncoding} is the second most common param.
|
||||
if (name == OS_PARAM_INPUT_ENCODING)
|
||||
return aEngine.queryCharset;
|
||||
|
||||
var official;
|
||||
if (Services.prefs.getBoolPref(BROWSER_SEARCH_PREF + "official", MOZ_OFFICIAL))
|
||||
official = "official";
|
||||
else
|
||||
official = "unofficial";
|
||||
// moz: parameters are only available for default search engines.
|
||||
if (name.startsWith("moz:") && aEngine._isDefault) {
|
||||
// {moz:locale} and {moz:distributionID} are common
|
||||
if (name == MOZ_PARAM_LOCALE)
|
||||
return getLocale();
|
||||
if (name == MOZ_PARAM_DIST_ID) {
|
||||
return Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "distributionID",
|
||||
Services.appinfo.distributionID || "");
|
||||
}
|
||||
// {moz:official} seems to have little use.
|
||||
if (name == MOZ_PARAM_OFFICIAL) {
|
||||
if (Services.prefs.getBoolPref(BROWSER_SEARCH_PREF + "official",
|
||||
AppConstants.MOZ_OFFICIAL_BRANDING))
|
||||
return "official";
|
||||
return "unofficial";
|
||||
}
|
||||
}
|
||||
|
||||
// Custom search parameters. These are only available to default search
|
||||
// engines.
|
||||
if (aEngine._isDefault) {
|
||||
value = value.replace(MOZ_PARAM_LOCALE, getLocale());
|
||||
value = value.replace(MOZ_PARAM_DIST_ID, distributionID);
|
||||
value = value.replace(MOZ_PARAM_OFFICIAL, official);
|
||||
}
|
||||
// Handle the less common OpenSearch parameters we're confident about.
|
||||
if (name == OS_PARAM_LANGUAGE)
|
||||
return getLocale() || OS_PARAM_LANGUAGE_DEF;
|
||||
if (name == OS_PARAM_OUTPUT_ENCODING)
|
||||
return OS_PARAM_OUTPUT_ENCODING_DEF;
|
||||
|
||||
// Insert the OpenSearch parameters we're confident about
|
||||
value = value.replace(OS_PARAM_USER_DEFINED, aSearchTerms);
|
||||
value = value.replace(OS_PARAM_INPUT_ENCODING, aEngine.queryCharset);
|
||||
value = value.replace(OS_PARAM_LANGUAGE,
|
||||
getLocale() || OS_PARAM_LANGUAGE_DEF);
|
||||
value = value.replace(OS_PARAM_OUTPUT_ENCODING,
|
||||
OS_PARAM_OUTPUT_ENCODING_DEF);
|
||||
// At this point, if a parameter is optional, just omit it.
|
||||
if (optional)
|
||||
return "";
|
||||
|
||||
// Replace any optional parameters
|
||||
value = value.replace(OS_PARAM_OPTIONAL, "");
|
||||
// Replace unsupported parameters that only have hardcoded default values.
|
||||
for (let param of OS_UNSUPPORTED_PARAMS) {
|
||||
if (name == param[0])
|
||||
return param[1];
|
||||
}
|
||||
|
||||
// Insert any remaining required params with our default values
|
||||
for (var i = 0; i < OS_UNSUPPORTED_PARAMS.length; ++i) {
|
||||
value = value.replace(OS_UNSUPPORTED_PARAMS[i][0],
|
||||
OS_UNSUPPORTED_PARAMS[i][1]);
|
||||
}
|
||||
|
||||
return value;
|
||||
// Don't replace unknown non-optional parameters.
|
||||
return match;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1148,11 +1152,11 @@ EngineURL.prototype = {
|
||||
postData.setData(stringStream);
|
||||
}
|
||||
|
||||
return new Submission(makeURI(url), postData);
|
||||
return new Submission(Services.io.newURI(url), postData);
|
||||
},
|
||||
|
||||
_getTermsParameterName: function SRCH_EURL__getTermsParameterName() {
|
||||
let queryParam = this.params.find(p => p.value == USER_DEFINED);
|
||||
let queryParam = this.params.find(p => p.value == "{" + USER_DEFINED + "}");
|
||||
return queryParam ? queryParam.name : "";
|
||||
},
|
||||
|
||||
@ -1495,9 +1499,9 @@ Engine.prototype = {
|
||||
* @param aRel [optional] only return URLs that with this rel value
|
||||
*/
|
||||
_getURLOfType: function SRCH_ENG__getURLOfType(aType, aRel) {
|
||||
for (var i = 0; i < this._urls.length; ++i) {
|
||||
if (this._urls[i].type == aType && (!aRel || this._urls[i]._hasRelation(aRel)))
|
||||
return this._urls[i];
|
||||
for (let url of this._urls) {
|
||||
if (url.type == aType && (!aRel || url._hasRelation(aRel)))
|
||||
return url;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -0,0 +1,67 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function run_test() {
|
||||
useHttpServer();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_paramSubstitution() {
|
||||
yield asyncInit();
|
||||
|
||||
let prefix = "http://test.moz/search?q=";
|
||||
let [engine] = yield addTestEngines([
|
||||
{ name: "test", details: ["", "test", "Search Test", "GET",
|
||||
prefix + "{searchTerms}"] },
|
||||
]);
|
||||
let url = engine.wrappedJSObject._getURLOfType("text/html");
|
||||
equal(url.template, prefix + "{searchTerms}");
|
||||
|
||||
let searchTerms = "fxsearch";
|
||||
function check(template, expected) {
|
||||
url.template = prefix + template;
|
||||
equal(engine.getSubmission(searchTerms).uri.spec, prefix + expected);
|
||||
}
|
||||
|
||||
// The same parameter can be used more than once.
|
||||
check("{searchTerms}/{searchTerms}", searchTerms + "/" + searchTerms);
|
||||
|
||||
// Optional parameters are replaced if we known them.
|
||||
check("{searchTerms?}", searchTerms);
|
||||
check("{unknownOptional?}", "");
|
||||
check("{unknownRequired}", "{unknownRequired}");
|
||||
|
||||
check("{language}", Services.locale.getRequestedLocale());
|
||||
check("{language?}", Services.locale.getRequestedLocale());
|
||||
|
||||
engine.wrappedJSObject._queryCharset = "UTF-8";
|
||||
check("{inputEncoding}", "UTF-8");
|
||||
check("{inputEncoding?}", "UTF-8");
|
||||
check("{outputEncoding}", "UTF-8");
|
||||
check("{outputEncoding?}", "UTF-8");
|
||||
|
||||
// 'Unsupported' parameters with hard coded values used only when the parameter is required.
|
||||
check("{count}", "20");
|
||||
check("{count?}", "");
|
||||
check("{startIndex}", "1");
|
||||
check("{startIndex?}", "");
|
||||
check("{startPage}", "1");
|
||||
check("{startPage?}", "");
|
||||
|
||||
// Test moz: parameters (only supported for built-in engines, ie _isDefault == true).
|
||||
check("{moz:distributionID}", "{moz:distributionID}");
|
||||
check("{moz:official}", "{moz:official}");
|
||||
check("{moz:locale}", "{moz:locale}");
|
||||
engine.wrappedJSObject._loadPath = "[app]"; // This will make _isDefault return true;
|
||||
check("{moz:distributionID}", "");
|
||||
Services.prefs.setCharPref("browser.search.distributionID", "xpcshell");
|
||||
check("{moz:distributionID}", "xpcshell");
|
||||
Services.prefs.setBoolPref("browser.search.official", true);
|
||||
check("{moz:official}", "official");
|
||||
Services.prefs.setBoolPref("browser.search.official", false);
|
||||
check("{moz:official}", "unofficial");
|
||||
check("{moz:locale}", Services.locale.getRequestedLocale());
|
||||
});
|
@ -99,3 +99,4 @@ tags = addons
|
||||
[test_chromeresource_icon1.js]
|
||||
[test_chromeresource_icon2.js]
|
||||
[test_engineUpdate.js]
|
||||
[test_paramSubstitution.js]
|
||||
|
@ -11318,12 +11318,12 @@
|
||||
"description": "Measures the number of milliseconds we spend waiting for sync message manager IPC messages to finish sending, keyed by message name. Note: only messages that wait for more than 500 microseconds are included in this probe."
|
||||
},
|
||||
"DISPLAY_ITEM_USAGE_COUNT": {
|
||||
"alert_emails": ["mchang@mozilla.com"],
|
||||
"alert_emails": ["mchang@mozilla.com", "gfx-telemetry@mozilla.com"],
|
||||
"bug_numbers": [1353521],
|
||||
"expires_in_version": "70",
|
||||
"expires_in_version": "56",
|
||||
"kind": "enumerated",
|
||||
"n_values": 99,
|
||||
"description": "Count of which display items are being used by type id"
|
||||
"description": "Count of which layout display items are being created. Display items are created by layout to determine what content to render. A full description is above the class definition for nsDisplayItem. The list of types is kept in nsDisplayItemTypes.h."
|
||||
},
|
||||
"TIME_TO_DOM_LOADING_MS": {
|
||||
"alert_emails": ["wpan@mozilla.com"],
|
||||
|
@ -383,6 +383,7 @@ patched_SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExce
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER sUnhandledExceptionFilter = nullptr;
|
||||
|
||||
static long
|
||||
@ -395,6 +396,15 @@ JitExceptionHandler(void *exceptionRecord, void *context)
|
||||
return sUnhandledExceptionFilter(&pointers);
|
||||
}
|
||||
|
||||
static void
|
||||
SetJitExceptionHandler()
|
||||
{
|
||||
sUnhandledExceptionFilter = GetUnhandledExceptionFilter();
|
||||
if (sUnhandledExceptionFilter)
|
||||
js::SetJitExceptionHandler(JitExceptionHandler);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reserve some VM space. In the event that we crash because VM space is
|
||||
* being leaked without leaking memory, freeing this space before taking
|
||||
@ -1801,9 +1811,7 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
|
||||
|
||||
#ifdef _WIN64
|
||||
// Tell JS about the new filter before we disable SetUnhandledExceptionFilter
|
||||
sUnhandledExceptionFilter = GetUnhandledExceptionFilter();
|
||||
if (sUnhandledExceptionFilter)
|
||||
js::SetJitExceptionHandler(JitExceptionHandler);
|
||||
SetJitExceptionHandler();
|
||||
#endif
|
||||
|
||||
// protect the crash reporter from being unloaded
|
||||
@ -3807,6 +3815,10 @@ SetRemoteExceptionHandler(const nsACString& crashPipe)
|
||||
nullptr);
|
||||
gExceptionHandler->set_handle_debug_exceptions(true);
|
||||
|
||||
#ifdef _WIN64
|
||||
SetJitExceptionHandler();
|
||||
#endif
|
||||
|
||||
mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
|
||||
|
||||
oldTerminateHandler = std::set_terminate(&TerminateHandler);
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsClipboard.h"
|
||||
#include "HeadlessClipboard.h"
|
||||
#include "nsSupportsPrimitives.h"
|
||||
#include "nsString.h"
|
||||
#include "nsReadableUtils.h"
|
||||
@ -20,6 +21,7 @@
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "imgIContainer.h"
|
||||
@ -38,6 +40,8 @@
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
using mozilla::dom::EncodingUtils;
|
||||
using namespace mozilla;
|
||||
|
||||
@ -79,6 +83,34 @@ selection_request_filter (GdkXEvent *gdk_xevent,
|
||||
GdkEvent *event,
|
||||
gpointer data);
|
||||
|
||||
namespace mozilla {
|
||||
namespace clipboard {
|
||||
StaticRefPtr<nsIClipboard> sInstance;
|
||||
}
|
||||
}
|
||||
/* static */ already_AddRefed<nsIClipboard>
|
||||
nsClipboard::GetInstance()
|
||||
{
|
||||
using namespace mozilla::clipboard;
|
||||
|
||||
if (!sInstance) {
|
||||
if (gfxPlatform::IsHeadless()) {
|
||||
sInstance = new widget::HeadlessClipboard();
|
||||
} else {
|
||||
RefPtr<nsClipboard> clipboard = new nsClipboard();
|
||||
nsresult rv = clipboard->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
sInstance = clipboard.forget();
|
||||
}
|
||||
ClearOnShutdown(&sInstance);
|
||||
}
|
||||
|
||||
RefPtr<nsIClipboard> service = sInstance.get();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
nsClipboard::nsClipboard()
|
||||
{
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ public:
|
||||
NS_DECL_NSICLIPBOARD
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static already_AddRefed<nsIClipboard> GetInstance();
|
||||
|
||||
// Make sure we are initialized, called from the factory
|
||||
// constructor
|
||||
nsresult Init (void);
|
||||
|
@ -73,7 +73,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
|
||||
#ifdef MOZ_X11
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceGTK, nsIdleServiceGTK::GetInstance)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIClipboard, nsClipboard::GetInstance)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDragService, nsDragService::GetInstance)
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
|
||||
@ -240,7 +240,7 @@ static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
|
||||
{ &kNS_SOUND_CID, false, nullptr, nsSoundConstructor, Module::MAIN_PROCESS_ONLY },
|
||||
{ &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor },
|
||||
#ifdef MOZ_X11
|
||||
{ &kNS_CLIPBOARD_CID, false, nullptr, nsClipboardConstructor, Module::MAIN_PROCESS_ONLY },
|
||||
{ &kNS_CLIPBOARD_CID, false, nullptr, nsIClipboardConstructor, Module::MAIN_PROCESS_ONLY },
|
||||
{ &kNS_CLIPBOARDHELPER_CID, false, nullptr, nsClipboardHelperConstructor },
|
||||
{ &kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceConstructor, Module::MAIN_PROCESS_ONLY },
|
||||
#endif
|
||||
|
126
widget/headless/HeadlessClipboard.cpp
Normal file
126
widget/headless/HeadlessClipboard.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "HeadlessClipboard.h"
|
||||
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
NS_IMPL_ISUPPORTS(HeadlessClipboard, nsIClipboard)
|
||||
|
||||
HeadlessClipboard::HeadlessClipboard()
|
||||
: mClipboard(MakeUnique<HeadlessClipboardData>())
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HeadlessClipboard::SetData(nsITransferable *aTransferable,
|
||||
nsIClipboardOwner *anOwner,
|
||||
int32_t aWhichClipboard)
|
||||
{
|
||||
if (aWhichClipboard != kGlobalClipboard) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Clear out the clipboard in order to set the new data.
|
||||
EmptyClipboard(aWhichClipboard);
|
||||
|
||||
// Only support plain text for now.
|
||||
nsCOMPtr<nsISupports> clip;
|
||||
uint32_t len;
|
||||
nsresult rv = aTransferable->GetTransferData(kUnicodeMime,
|
||||
getter_AddRefs(clip),
|
||||
&len);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsISupportsString> wideString = do_QueryInterface(clip);
|
||||
if (!wideString) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
nsAutoString utf16string;
|
||||
wideString->GetData(utf16string);
|
||||
mClipboard->SetText(utf16string);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HeadlessClipboard::GetData(nsITransferable *aTransferable,
|
||||
int32_t aWhichClipboard)
|
||||
{
|
||||
if (aWhichClipboard != kGlobalClipboard) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISupportsString> dataWrapper =
|
||||
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
|
||||
rv = dataWrapper->SetData(mClipboard->GetText());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsISupports> genericDataWrapper = do_QueryInterface(dataWrapper);
|
||||
uint32_t len = mClipboard->GetText().Length() * sizeof(char16_t);
|
||||
rv = aTransferable->SetTransferData(kUnicodeMime, genericDataWrapper, len);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HeadlessClipboard::EmptyClipboard(int32_t aWhichClipboard)
|
||||
{
|
||||
if (aWhichClipboard != kGlobalClipboard) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
mClipboard->Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HeadlessClipboard::HasDataMatchingFlavors(const char **aFlavorList,
|
||||
uint32_t aLength, int32_t aWhichClipboard,
|
||||
bool *aHasType)
|
||||
{
|
||||
*aHasType = false;
|
||||
if (aWhichClipboard != kGlobalClipboard) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
// Retrieve the union of all aHasType in aFlavorList
|
||||
for (uint32_t i = 0; i < aLength; ++i) {
|
||||
const char *flavor = aFlavorList[i];
|
||||
if (!flavor) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(flavor, kUnicodeMime) && mClipboard->HasText()) {
|
||||
*aHasType = true;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HeadlessClipboard::SupportsSelectionClipboard(bool *aIsSupported)
|
||||
{
|
||||
*aIsSupported = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HeadlessClipboard::SupportsFindClipboard(bool* _retval)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
35
widget/headless/HeadlessClipboard.h
Normal file
35
widget/headless/HeadlessClipboard.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#ifndef mozilla_widget_HeadlessClipboard_h
|
||||
#define mozilla_widget_HeadlessClipboard_h
|
||||
|
||||
#include "nsIClipboard.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "HeadlessClipboardData.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
class HeadlessClipboard final : public nsIClipboard
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICLIPBOARD
|
||||
|
||||
HeadlessClipboard();
|
||||
|
||||
protected:
|
||||
~HeadlessClipboard() {}
|
||||
|
||||
private:
|
||||
UniquePtr<HeadlessClipboardData> mClipboard;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
35
widget/headless/HeadlessClipboardData.cpp
Normal file
35
widget/headless/HeadlessClipboardData.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "HeadlessClipboardData.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
void
|
||||
HeadlessClipboardData::SetText(const nsAString &aText)
|
||||
{
|
||||
mPlain = aText;
|
||||
}
|
||||
|
||||
bool
|
||||
HeadlessClipboardData::HasText() const
|
||||
{
|
||||
return !mPlain.IsEmpty();
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
HeadlessClipboardData::GetText() const
|
||||
{
|
||||
return mPlain;
|
||||
}
|
||||
|
||||
void
|
||||
HeadlessClipboardData::Clear()
|
||||
{
|
||||
mPlain.Truncate(0);
|
||||
}
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
35
widget/headless/HeadlessClipboardData.h
Normal file
35
widget/headless/HeadlessClipboardData.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_widget_HeadlessClipboardData_h
|
||||
#define mozilla_widget_HeadlessClipboardData_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
class HeadlessClipboardData final
|
||||
{
|
||||
public:
|
||||
explicit HeadlessClipboardData() = default;
|
||||
~HeadlessClipboardData() = default;
|
||||
|
||||
// For text/plain
|
||||
void SetText(const nsAString &aText);
|
||||
bool HasText() const;
|
||||
const nsAString& GetText() const;
|
||||
|
||||
// For other APIs
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
nsAutoString mPlain;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_widget_HeadlessClipboardData_h
|
@ -12,9 +12,12 @@ DIRS += ['tests']
|
||||
LOCAL_INCLUDES += [
|
||||
'/widget',
|
||||
'/widget/gtk',
|
||||
'/widget/headless',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'HeadlessClipboard.cpp',
|
||||
'HeadlessClipboardData.cpp',
|
||||
'HeadlessLookAndFeel.cpp',
|
||||
'HeadlessWidget.cpp',
|
||||
]
|
||||
|
47
widget/headless/tests/test_headless_clipboard.js
Normal file
47
widget/headless/tests/test_headless_clipboard.js
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function getString(clipboard) {
|
||||
var str = "";
|
||||
|
||||
// Create transferable that will transfer the text.
|
||||
var trans = Cc["@mozilla.org/widget/transferable;1"]
|
||||
.createInstance(Ci.nsITransferable);
|
||||
trans.init(null);
|
||||
trans.addDataFlavor("text/unicode");
|
||||
|
||||
clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
|
||||
|
||||
try {
|
||||
var data = {};
|
||||
var dataLen = {};
|
||||
trans.getTransferData("text/unicode", data, dataLen);
|
||||
|
||||
if (data) {
|
||||
data = data.value.QueryInterface(Ci.nsISupportsString);
|
||||
str = data.data.substring(0, dataLen.value / 2);
|
||||
}
|
||||
} catch (ex) {
|
||||
// If the clipboard is empty getTransferData will throw.
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
add_task(function* test_clipboard() {
|
||||
let clipboard = Cc['@mozilla.org/widget/clipboard;1']
|
||||
.getService(Ci.nsIClipboard);
|
||||
|
||||
// Test copy.
|
||||
const data = "random number: " + Math.random();
|
||||
let helper = Cc['@mozilla.org/widget/clipboardhelper;1']
|
||||
.getService(Ci.nsIClipboardHelper);
|
||||
helper.copyString(data);
|
||||
equal(getString(clipboard), data, 'Data was successfully copied.');
|
||||
|
||||
clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
|
||||
equal(getString(clipboard), '', 'Data was successfully cleared.');
|
||||
});
|
@ -1,6 +1,9 @@
|
||||
[test_headless.js]
|
||||
[DEFAULT]
|
||||
skip-if = os != "linux"
|
||||
headless = true
|
||||
|
||||
[test_headless_clipboard.js]
|
||||
[test_headless.js]
|
||||
support-files =
|
||||
headless.html
|
||||
headless_button.html
|
||||
|
Loading…
Reference in New Issue
Block a user