mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge fx-team to mozilla-central a=merge
This commit is contained in:
commit
1d9467746c
@ -219,7 +219,7 @@ skip-if = e10s
|
||||
[browser_bug565667.js]
|
||||
run-if = toolkit == "cocoa"
|
||||
[browser_bug567306.js]
|
||||
skip-if = e10s
|
||||
skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
|
||||
[browser_bug575561.js]
|
||||
[browser_bug575830.js]
|
||||
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
|
||||
|
@ -13,8 +13,8 @@ function test() {
|
||||
whenNewWindowLoaded(undefined, function (win) {
|
||||
whenDelayedStartupFinished(win, function () {
|
||||
let selectedBrowser = win.gBrowser.selectedBrowser;
|
||||
selectedBrowser.addEventListener("pageshow", function() {
|
||||
selectedBrowser.removeEventListener("pageshow", arguments.callee, true);
|
||||
selectedBrowser.addEventListener("pageshow", function pageshowListener() {
|
||||
selectedBrowser.removeEventListener("pageshow", pageshowListener, true);
|
||||
ok(true, "pageshow listener called: " + win.content.location);
|
||||
waitForFocus(function () {
|
||||
onFocus(win);
|
||||
@ -39,7 +39,11 @@ function onFocus(win) {
|
||||
ok(!win.gFindBarInitialized, "find bar is not yet initialized");
|
||||
let findBar = win.gFindBar;
|
||||
selectText(win.content);
|
||||
findBar.onFindCommand();
|
||||
|
||||
findBar.onFindCommand().then(onInitialized.bind(null, findBar, win));
|
||||
}
|
||||
|
||||
function onInitialized(findBar, win) {
|
||||
// When the OS supports the Find Clipboard (OSX), the find field value is
|
||||
// persisted across Fx sessions, thus not useful to test.
|
||||
if (!HasFindClipboard)
|
||||
|
@ -2,13 +2,6 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("");
|
||||
|
||||
var gTab = null;
|
||||
|
||||
function load(url, cb) {
|
||||
|
@ -28,7 +28,7 @@ browser.jar:
|
||||
content/browser/preferences/languages.js
|
||||
* content/browser/preferences/main.xul
|
||||
* content/browser/preferences/main.js
|
||||
* content/browser/preferences/permissions.xul
|
||||
content/browser/preferences/permissions.xul
|
||||
* content/browser/preferences/permissions.js
|
||||
* content/browser/preferences/preferences.xul
|
||||
content/browser/preferences/preferences.js
|
||||
@ -48,5 +48,5 @@ browser.jar:
|
||||
content/browser/preferences/search.js
|
||||
* content/browser/preferences/tabs.xul
|
||||
* content/browser/preferences/tabs.js
|
||||
* content/browser/preferences/translation.xul
|
||||
content/browser/preferences/translation.xul
|
||||
content/browser/preferences/translation.js
|
||||
|
@ -1,9 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# 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/.
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
@ -29,7 +28,7 @@
|
||||
<keyset>
|
||||
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
|
||||
<vbox class="contentPane" flex="1">
|
||||
<description id="permissionsText" control="url"/>
|
||||
<separator class="thin"/>
|
||||
|
@ -1,9 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# 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/.
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
|
@ -2104,8 +2104,7 @@ NetworkDetailsView.prototype = {
|
||||
// 2) come from a target that provides security information.
|
||||
let hasSecurityInfo = aData.securityState &&
|
||||
aData.securityState !== "insecure";
|
||||
|
||||
$("#security-tab").hidden = !hasSecurityInfo;
|
||||
this.sidebar.toggleTab(hasSecurityInfo, "security-tab", "security-tabpanel");
|
||||
|
||||
// Switch to the "Headers" tabpanel if the "Preview" previously selected
|
||||
// and this is not an HTML response or "Security" was selected but this
|
||||
|
@ -39,7 +39,8 @@ support-files =
|
||||
[browser_perf_recordings-io-01.js]
|
||||
[browser_perf_recordings-io-02.js]
|
||||
[browser_perf_recordings-io-03.js]
|
||||
[browser_perf_recordings-io-04.js]
|
||||
[browser_perf-recording-selected-01.js]
|
||||
[browser_perf-recording-selected-02.js]
|
||||
[browser_perf-recording-selected-03.js]
|
||||
[browser_perf_recordings-io-04.js]
|
||||
[browser_perf-recording-selected-04.js]
|
||||
|
@ -0,0 +1,29 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that all components get rerendered for a profile when switching.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { $, EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
let rerender = waitForWidgetsRendered(panel);
|
||||
RecordingsView.selectedIndex = 0;
|
||||
yield rerender;
|
||||
|
||||
rerender = waitForWidgetsRendered(panel);
|
||||
RecordingsView.selectedIndex = 1;
|
||||
yield rerender;
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -12,13 +12,12 @@ let CallTreeView = {
|
||||
*/
|
||||
initialize: function () {
|
||||
this._callTree = $(".call-tree-cells-container");
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onLink = this._onLink.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
},
|
||||
@ -27,8 +26,8 @@ let CallTreeView = {
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
},
|
||||
@ -47,20 +46,10 @@ let CallTreeView = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped.
|
||||
* Called when recording is stopped or has been selected.
|
||||
*/
|
||||
_onRecordingStopped: function () {
|
||||
let profilerData = PerformanceController.getProfilerData();
|
||||
this.render(profilerData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a recording has been selected.
|
||||
*/
|
||||
_onRecordingSelected: function (_, recording) {
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
// If not recording, then this recording is done and we can render all of it
|
||||
// Otherwise, TODO in bug 1120699 will hide the details view altogether if
|
||||
// this is still recording.
|
||||
if (!recording.isRecording()) {
|
||||
let profilerData = recording.getProfilerData();
|
||||
this.render(profilerData);
|
||||
|
@ -12,14 +12,15 @@ let FlameGraphView = {
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
|
||||
this.graph = new FlameGraph($("#flamegraph-view"));
|
||||
this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
|
||||
yield this.graph.ready();
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
}),
|
||||
@ -28,7 +29,8 @@ let FlameGraphView = {
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
},
|
||||
@ -52,11 +54,14 @@ let FlameGraphView = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped.
|
||||
* Called when recording is stopped or selected.
|
||||
*/
|
||||
_onRecordingStopped: function () {
|
||||
let profilerData = PerformanceController.getProfilerData();
|
||||
this.render(profilerData);
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
// If not recording, then this recording is done and we can render all of it
|
||||
if (!recording.isRecording()) {
|
||||
let profilerData = recording.getProfilerData();
|
||||
this.render(profilerData);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -12,8 +12,7 @@ let WaterfallView = {
|
||||
*/
|
||||
initialize: Task.async(function *() {
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
|
||||
@ -25,8 +24,8 @@ let WaterfallView = {
|
||||
this.details.on("resize", this._onResize);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
|
||||
this.waterfall.recalculateBounds();
|
||||
}),
|
||||
@ -40,8 +39,8 @@ let WaterfallView = {
|
||||
this.details.off("resize", this._onResize);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -65,16 +64,9 @@ let WaterfallView = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording stops.
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStopped: function () {
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a recording is selected.
|
||||
*/
|
||||
_onRecordingSelected: function (_, recording) {
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
|
@ -203,7 +203,9 @@ PreviewController.prototype = {
|
||||
// Resizes the canvasPreview to 0x0, essentially freeing its memory.
|
||||
// updateCanvasPreview() will detect the size mismatch as a resize event
|
||||
// the next time it is called.
|
||||
resetCanvasPreview: function () this.resizeCanvasPreview(0, 0),
|
||||
resetCanvasPreview: function () {
|
||||
this.resizeCanvasPreview(0, 0);
|
||||
},
|
||||
|
||||
resizeCanvasPreview: function (width, height) {
|
||||
this.canvasPreview.width = width;
|
||||
|
@ -199,7 +199,7 @@ browser.jar:
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/buttons@2x.png (downloads/buttons@2x.png)
|
||||
* skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
|
||||
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
|
||||
|
@ -93,9 +93,11 @@
|
||||
testNormalFind();
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "Failed to close findbar after testNormalFind");
|
||||
yield openFindbar();
|
||||
testNormalFindWithComposition();
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "findbar should be hidden after testNormalFindWithComposition");
|
||||
yield openFindbar();
|
||||
testAutoCaseSensitivityUI();
|
||||
testQuickFindText();
|
||||
gFindBar.close();
|
||||
@ -103,7 +105,8 @@
|
||||
testFindWithHighlight();
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "Failed to close findbar after testFindWithHighlight");
|
||||
testFindbarSelection();
|
||||
yield Task.spawn(testFindbarSelection);
|
||||
ok(gFindBar.hidden, "Failed to close findbar after testFindbarSelection");
|
||||
testDrop();
|
||||
testQuickFindLink();
|
||||
if (gHasFindClipboard) {
|
||||
@ -112,17 +115,18 @@
|
||||
yield testFindCountUI();
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "Failed to close findbar after testFindCountUI");
|
||||
yield openFindbar();
|
||||
yield testFindAfterCaseChanged();
|
||||
gFindBar.close();
|
||||
yield openFindbar();
|
||||
yield testFailedStringReset();
|
||||
gFindBar.close();
|
||||
yield testQuickFindClose();
|
||||
finish();
|
||||
});
|
||||
|
||||
function testFindbarSelection() {
|
||||
function* testFindbarSelection() {
|
||||
function checkFindbarState(aTestName, aExpSelection) {
|
||||
document.getElementById("cmd_find").doCommand();
|
||||
ok(!gFindBar.hidden, "testFindbarSelection: failed to open findbar: " + aTestName);
|
||||
ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
|
||||
"testFindbarSelection: find field is not focused: " + aTestName);
|
||||
@ -145,16 +149,19 @@
|
||||
cRange.setEnd(cH2, 1);
|
||||
cSelection.removeAllRanges();
|
||||
cSelection.addRange(cRange);
|
||||
yield openFindbar();
|
||||
checkFindbarState("plain text", SEARCH_TEXT);
|
||||
|
||||
// test nsIDOMNSEditableElement with selection
|
||||
var textInput = gBrowser.contentDocument.getElementById("text");
|
||||
textInput.focus();
|
||||
textInput.select();
|
||||
yield openFindbar();
|
||||
checkFindbarState("text input", SAMPLE_TEXT);
|
||||
|
||||
// test non-editable nsIDOMNSEditableElement (button)
|
||||
gBrowser.contentDocument.getElementById("button").focus();
|
||||
yield openFindbar();
|
||||
checkFindbarState("button", "");
|
||||
}
|
||||
|
||||
@ -226,9 +233,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
function testNormalFindWithComposition() {
|
||||
function openFindbar() {
|
||||
document.getElementById("cmd_find").doCommand();
|
||||
return gFindBar._startFindDeferred.promise;
|
||||
}
|
||||
|
||||
function testNormalFindWithComposition() {
|
||||
ok(!gFindBar.hidden, "testNormalFindWithComposition: findbar should be open");
|
||||
ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
|
||||
"testNormalFindWithComposition: find field should be focused");
|
||||
@ -261,7 +271,7 @@
|
||||
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
|
||||
ok(gBrowser.contentWindow.getSelection().toString().toLowerCase() == searchStr,
|
||||
is(gBrowser.contentWindow.getSelection().toString().toLowerCase(), searchStr,
|
||||
"testNormalFindWithComposition: text should be found after committing composition");
|
||||
testClipboardSearchString(gBrowser.contentWindow.getSelection().toString());
|
||||
|
||||
@ -273,7 +283,6 @@
|
||||
function testAutoCaseSensitivityUI() {
|
||||
var matchCaseCheckbox = gFindBar.getElement("find-case-sensitive");
|
||||
var matchCaseLabel = gFindBar.getElement("match-case-status");
|
||||
document.getElementById("cmd_find").doCommand();
|
||||
ok(!matchCaseCheckbox.hidden, "match case box is hidden in manual mode");
|
||||
ok(matchCaseLabel.hidden, "match case label is visible in manual mode");
|
||||
|
||||
@ -331,10 +340,21 @@
|
||||
//clearFocus();
|
||||
gFindBar._findField.value = "";
|
||||
|
||||
// For this test, we want to closely control the selection. The easiest
|
||||
// way to do so is to replace the implementation of
|
||||
// Finder.getInitialSelection with a no-op and call the findbar's callback
|
||||
// (onCurrentSelection(..., true)) ourselves with our hand-picked
|
||||
// selection.
|
||||
let oldGetInitialSelection = gFindBar.browser.finder.getInitialSelection;
|
||||
let searchStr;
|
||||
gFindBar.browser.finder.getInitialSelection = function(){};
|
||||
|
||||
let findCommand = document.getElementById("cmd_find");
|
||||
findCommand.doCommand();
|
||||
|
||||
let searchStr = "e";
|
||||
gFindBar.onCurrentSelection("", true);
|
||||
|
||||
searchStr = "e";
|
||||
enterStringIntoFindField(searchStr);
|
||||
|
||||
let a = gFindBar._findField.value;
|
||||
@ -342,11 +362,11 @@
|
||||
let c = gFindBar._browser.finder.searchString;
|
||||
ok(a == b && b == c, "testFindWithHighlight 1: " + a + ", " + b + ", " + c + ".");
|
||||
|
||||
let oldGetInitialSelection = gFindBar._getInitialSelection;
|
||||
searchStr = "t";
|
||||
gFindBar._getInitialSelection = () => searchStr;
|
||||
findCommand.doCommand();
|
||||
gFindBar._getInitialSelection = oldGetInitialSelection;
|
||||
|
||||
gFindBar.onCurrentSelection(searchStr, true);
|
||||
gFindBar.browser.finder.getInitialSelection = oldGetInitialSelection;
|
||||
|
||||
a = gFindBar._findField.value;
|
||||
b = gFindBar._browser.finder._fastFind.searchString;
|
||||
@ -472,7 +492,6 @@
|
||||
// See bug 1051187.
|
||||
function testFindAfterCaseChanged() {
|
||||
let deferred = Promise.defer();
|
||||
document.getElementById("cmd_find").doCommand();
|
||||
|
||||
// Search to set focus on "Text Test" so that searching for "t" selects first
|
||||
// (upper case!) "T".
|
||||
@ -499,7 +518,6 @@
|
||||
// 2. Uncheck case sensitivity button to match the string.
|
||||
function testFailedStringReset(aCallback) {
|
||||
let deferred = Promise.defer();
|
||||
document.getElementById("cmd_find").doCommand();
|
||||
|
||||
var prefsvc = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
|
@ -215,10 +215,15 @@
|
||||
<field name="_flashFindBar">0</field>
|
||||
<field name="_initialFlashFindBarCount">6</field>
|
||||
|
||||
<!--
|
||||
- For tests that need to know when the find bar is finished
|
||||
- initializing, we store a promise to notify on.
|
||||
-->
|
||||
<field name="_startFindDeferred">null</field>
|
||||
|
||||
<property name="prefillWithSelection"
|
||||
onget="return this.getAttribute('prefillwithselection') != 'false'"
|
||||
onset="this.setAttribute('prefillwithselection', val); return val;"/>
|
||||
<field name="_selectionMaxLen">150</field>
|
||||
|
||||
<method name="getElement">
|
||||
<parameter name="aAnonymousID"/>
|
||||
@ -1026,43 +1031,6 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_getInitialSelection">
|
||||
<body><![CDATA[
|
||||
let focusedElement = document.commandDispatcher.focusedElement;
|
||||
let selText;
|
||||
|
||||
if (focusedElement instanceof Components.interfaces.nsIDOMNSEditableElement &&
|
||||
focusedElement.editor &&
|
||||
focusedElement.ownerDocument.defaultView.top == this._browser.contentWindow)
|
||||
{
|
||||
// The user may have a selection in an input or textarea
|
||||
selText = focusedElement.editor.selectionController
|
||||
.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL)
|
||||
.toString();
|
||||
}
|
||||
else {
|
||||
// Look for any selected text on the actual page
|
||||
let focusedWindow = document.commandDispatcher.focusedWindow;
|
||||
if (focusedWindow.top == this._browser.contentWindow)
|
||||
selText = focusedWindow.getSelection().toString();
|
||||
}
|
||||
|
||||
if (!selText)
|
||||
return "";
|
||||
|
||||
// Process our text to get rid of unwanted characters
|
||||
if (selText.length > this._selectionMaxLen) {
|
||||
let pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
|
||||
pattern.test(selText);
|
||||
selText = RegExp.lastMatch;
|
||||
}
|
||||
return selText.replace(/^\s+/, "")
|
||||
.replace(/\s+$/, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.substr(0, this._selectionMaxLen);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_dispatchFindEvent">
|
||||
<parameter name="aType"/>
|
||||
<parameter name="aFindPrevious"/>
|
||||
@ -1102,28 +1070,29 @@
|
||||
--this._flashFindBar);
|
||||
}
|
||||
|
||||
let {PromiseUtils} =
|
||||
Components.utils.import("resource://gre/modules/PromiseUtils.jsm", {});
|
||||
this._startFindDeferred = PromiseUtils.defer();
|
||||
let startFindPromise = this._startFindDeferred.promise;
|
||||
|
||||
if (this.prefillWithSelection)
|
||||
userWantsPrefill =
|
||||
prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
|
||||
|
||||
let initialString = null;
|
||||
if (this.prefillWithSelection && userWantsPrefill)
|
||||
initialString = this._getInitialSelection();
|
||||
#ifdef XP_MACOSX
|
||||
if (!initialString) {
|
||||
let clipboardSearchString = this.browser.finder.clipboardSearchString;
|
||||
if (clipboardSearchString)
|
||||
initialString = clipboardSearchString;
|
||||
if (this.prefillWithSelection && userWantsPrefill) {
|
||||
// NB: We have to focus this._findField here so tests that send
|
||||
// key events can open and close the find bar synchronously.
|
||||
this._findField.focus();
|
||||
this.browser.finder.getInitialSelection();
|
||||
return startFindPromise;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (initialString)
|
||||
this._findField.value = initialString;
|
||||
|
||||
this._enableFindButtons(!!this._findField.value);
|
||||
|
||||
this._findField.select();
|
||||
this._findField.focus();
|
||||
// If userWantsPrefill is false but prefillWithSelection is true,
|
||||
// then we might need to check the selection clipboard. Call
|
||||
// onCurrentSelection to do so.
|
||||
// Note: this.onCurrentSelection clears this._startFindDeferred.
|
||||
this.onCurrentSelection("", true);
|
||||
return startFindPromise;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -1135,7 +1104,7 @@
|
||||
-->
|
||||
<method name="onFindCommand">
|
||||
<body><![CDATA[
|
||||
this.startFind(this.FIND_NORMAL);
|
||||
return this.startFind(this.FIND_NORMAL);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -1148,10 +1117,8 @@
|
||||
<parameter name="aFindPrevious"/>
|
||||
<body><![CDATA[
|
||||
let findString = this._browser.finder.searchString || this._findField.value;
|
||||
if (!findString) {
|
||||
this.startFind();
|
||||
return;
|
||||
}
|
||||
if (!findString)
|
||||
return this.startFind();
|
||||
|
||||
// We dispatch the findAgain event here instead of in _findAgain since
|
||||
// if there is a find event handler that prevents the default then
|
||||
@ -1265,6 +1232,33 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onCurrentSelection">
|
||||
<parameter name="aSelectionString" />
|
||||
<parameter name="aIsInitialSelection" />
|
||||
<body><![CDATA[
|
||||
#ifdef XP_MACOSX
|
||||
if (aIsInitialSelection && !aSelectionString) {
|
||||
let clipboardSearchString = this.browser.finder.clipboardSearchString;
|
||||
if (clipboardSearchString)
|
||||
aSelectionString = clipboardSearchString;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aSelectionString)
|
||||
this._findField.value = aSelectionString;
|
||||
|
||||
if (aIsInitialSelection) {
|
||||
this._enableFindButtons(!!this._findField.value);
|
||||
this._findField.select();
|
||||
this._findField.focus();
|
||||
if (this._startFindDeferred) {
|
||||
this._startFindDeferred.resolve();
|
||||
this._startFindDeferred = null;
|
||||
}
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
- This handler may cancel a request to focus content by returning |false|
|
||||
- explicitly.
|
||||
|
@ -1,8 +1,9 @@
|
||||
// vim: set ts=2 sw=2 sts=2 tw=80:
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Finder"];
|
||||
this.EXPORTED_SYMBOLS = ["Finder","GetClipboardSearchString"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
@ -24,6 +25,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "ClipboardHelper",
|
||||
"nsIClipboardHelper");
|
||||
|
||||
const kHighlightIterationSizeMax = 100;
|
||||
const kSelectionMaxLen = 150;
|
||||
|
||||
function Finder(docShell) {
|
||||
this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
|
||||
@ -90,31 +92,10 @@ Finder.prototype = {
|
||||
},
|
||||
|
||||
get clipboardSearchString() {
|
||||
let searchString = "";
|
||||
if (!Clipboard.supportsFindClipboard())
|
||||
return searchString;
|
||||
|
||||
try {
|
||||
let trans = Cc["@mozilla.org/widget/transferable;1"]
|
||||
.createInstance(Ci.nsITransferable);
|
||||
trans.init(this._getWindow()
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext));
|
||||
trans.addDataFlavor("text/unicode");
|
||||
|
||||
Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
|
||||
|
||||
let data = {};
|
||||
let dataLen = {};
|
||||
trans.getTransferData("text/unicode", data, dataLen);
|
||||
if (data.value) {
|
||||
data = data.value.QueryInterface(Ci.nsISupportsString);
|
||||
searchString = data.toString();
|
||||
}
|
||||
} catch (ex) {}
|
||||
|
||||
return searchString;
|
||||
return GetClipboardSearchString(this._getWindow()
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext));
|
||||
},
|
||||
|
||||
set clipboardSearchString(aSearchString) {
|
||||
@ -165,12 +146,8 @@ Finder.prototype = {
|
||||
* selected text in the window, on supported platforms (i.e. OSX).
|
||||
*/
|
||||
setSearchStringToSelection: function() {
|
||||
// Find the selected text.
|
||||
let selection = this._getWindow().getSelection();
|
||||
// Don't go for empty selections.
|
||||
if (!selection.rangeCount)
|
||||
return null;
|
||||
let searchString = (selection.toString() || "").trim();
|
||||
let searchString = this.getActiveSelectionText();
|
||||
|
||||
// Empty strings are rather useless to search for.
|
||||
if (!searchString.length)
|
||||
return null;
|
||||
@ -201,6 +178,44 @@ Finder.prototype = {
|
||||
}
|
||||
}),
|
||||
|
||||
getInitialSelection: function() {
|
||||
this._getWindow().setTimeout(() => {
|
||||
let initialSelection = this.getActiveSelectionText();
|
||||
for (let l of this._listeners) {
|
||||
try {
|
||||
l.onCurrentSelection(initialSelection, true);
|
||||
} catch (ex) {}
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
|
||||
getActiveSelectionText: function() {
|
||||
let focusedWindow = {};
|
||||
let focusedElement =
|
||||
Services.focus.getFocusedElementForWindow(this._getWindow(), true,
|
||||
focusedWindow);
|
||||
focusedWindow = focusedWindow.value;
|
||||
|
||||
let selText;
|
||||
|
||||
if (focusedElement instanceof Ci.nsIDOMNSEditableElement &&
|
||||
focusedElement.editor) {
|
||||
// The user may have a selection in an input or textarea.
|
||||
selText = focusedElement.editor.selectionController
|
||||
.getSelection(Ci.nsISelectionController.SELECTION_NORMAL)
|
||||
.toString();
|
||||
} else {
|
||||
// Look for any selected text on the actual page.
|
||||
selText = focusedWindow.getSelection().toString();
|
||||
}
|
||||
|
||||
if (!selText)
|
||||
return "";
|
||||
|
||||
// Process our text to get rid of unwanted characters.
|
||||
return selText.trim().replace(/\s+/g, " ").substr(0, kSelectionMaxLen);
|
||||
},
|
||||
|
||||
enableSelection: function() {
|
||||
this._fastFind.setSelectionModeAndRepaint(Ci.nsISelectionController.SELECTION_ON);
|
||||
this._restoreOriginalOutline();
|
||||
@ -647,8 +662,14 @@ Finder.prototype = {
|
||||
|
||||
_getSelectionController: function(aWindow) {
|
||||
// display: none iframes don't have a selection controller, see bug 493658
|
||||
if (!aWindow.innerWidth || !aWindow.innerHeight)
|
||||
try {
|
||||
if (!aWindow.innerWidth || !aWindow.innerHeight)
|
||||
return null;
|
||||
} catch (e) {
|
||||
// If getting innerWidth or innerHeight throws, we can't get a selection
|
||||
// controller.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Yuck. See bug 138068.
|
||||
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
@ -1002,3 +1023,28 @@ Finder.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
function GetClipboardSearchString(aLoadContext) {
|
||||
let searchString = "";
|
||||
if (!Clipboard.supportsFindClipboard())
|
||||
return searchString;
|
||||
|
||||
try {
|
||||
let trans = Cc["@mozilla.org/widget/transferable;1"]
|
||||
.createInstance(Ci.nsITransferable);
|
||||
trans.init(aLoadContext);
|
||||
trans.addDataFlavor("text/unicode");
|
||||
|
||||
Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
|
||||
|
||||
let data = {};
|
||||
let dataLen = {};
|
||||
trans.getTransferData("text/unicode", data, dataLen);
|
||||
if (data.value) {
|
||||
data = data.value.QueryInterface(Ci.nsISupportsString);
|
||||
searchString = data.toString();
|
||||
}
|
||||
} catch (ex) {}
|
||||
|
||||
return searchString;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
// vim: set ts=2 sw=2 sts=2 et tw=80: */
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
@ -11,39 +12,53 @@ const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "GetClipboardSearchString",
|
||||
() => Cu.import("resource://gre/modules/Finder.jsm", {}).GetClipboardSearchString
|
||||
);
|
||||
|
||||
function RemoteFinder(browser) {
|
||||
this._browser = browser;
|
||||
this._listeners = [];
|
||||
this._listeners = new Set();
|
||||
this._searchString = null;
|
||||
|
||||
this._browser.messageManager.addMessageListener("Finder:Result", this);
|
||||
this._browser.messageManager.addMessageListener("Finder:MatchesResult", this);
|
||||
this._browser.messageManager.sendAsyncMessage("Finder:Initialize");
|
||||
let mm = this._browser.messageManager;
|
||||
mm.addMessageListener("Finder:Result", this);
|
||||
mm.addMessageListener("Finder:MatchesResult", this);
|
||||
mm.addMessageListener("Finder:CurrentSelectionResult",this);
|
||||
mm.sendAsyncMessage("Finder:Initialize");
|
||||
}
|
||||
|
||||
RemoteFinder.prototype = {
|
||||
addResultListener: function (aListener) {
|
||||
if (this._listeners.indexOf(aListener) === -1)
|
||||
this._listeners.push(aListener);
|
||||
this._listeners.add(aListener);
|
||||
},
|
||||
|
||||
removeResultListener: function (aListener) {
|
||||
this._listeners = this._listeners.filter(l => l != aListener);
|
||||
this._listeners.delete(aListener);
|
||||
},
|
||||
|
||||
receiveMessage: function (aMessage) {
|
||||
// Only Finder:Result messages have the searchString field.
|
||||
if (aMessage.name == "Finder:Result") {
|
||||
this._searchString = aMessage.data.searchString;
|
||||
let callback;
|
||||
let params;
|
||||
switch (aMessage.name) {
|
||||
case "Finder:Result":
|
||||
this._searchString = aMessage.data.searchString;
|
||||
callback = "onFindResult";
|
||||
params = [ aMessage.data ];
|
||||
break;
|
||||
case "Finder:MatchesResult":
|
||||
callback = "onMatchesCountResult";
|
||||
params = [ aMessage.data ];
|
||||
break;
|
||||
case "Finder:CurrentSelectionResult":
|
||||
callback = "onCurrentSelection";
|
||||
params = [ aMessage.data.selection, aMessage.data.initial ];
|
||||
break;
|
||||
}
|
||||
|
||||
// The parent can receive either one of the two types of message.
|
||||
for (let l of this._listeners) {
|
||||
if (aMessage.name == "Finder:Result") {
|
||||
l.onFindResult(aMessage.data);
|
||||
} else if (aMessage.name == "Finder:MatchesResult") {
|
||||
l.onMatchesCountResult(aMessage.data);
|
||||
}
|
||||
l[callback].apply(l, params);
|
||||
}
|
||||
},
|
||||
|
||||
@ -51,11 +66,23 @@ RemoteFinder.prototype = {
|
||||
return this._searchString;
|
||||
},
|
||||
|
||||
get clipboardSearchString() {
|
||||
return GetClipboardSearchString(this._browser.loadContext);
|
||||
},
|
||||
|
||||
setSearchStringToSelection() {
|
||||
this._browser.messageManager.sendAsyncMessage("Finder:SetSearchStringToSelection", {});
|
||||
},
|
||||
|
||||
set caseSensitive(aSensitive) {
|
||||
this._browser.messageManager.sendAsyncMessage("Finder:CaseSensitive",
|
||||
{ caseSensitive: aSensitive });
|
||||
},
|
||||
|
||||
getInitialSelection: function() {
|
||||
this._browser.messageManager.sendAsyncMessage("Finder:GetInitialSelection", {});
|
||||
},
|
||||
|
||||
fastFind: function (aSearchString, aLinksOnly) {
|
||||
this._browser.messageManager.sendAsyncMessage("Finder:FastFind",
|
||||
{ searchString: aSearchString,
|
||||
@ -127,6 +154,8 @@ RemoteFinderListener.prototype = {
|
||||
"Finder:CaseSensitive",
|
||||
"Finder:FastFind",
|
||||
"Finder:FindAgain",
|
||||
"Finder:SetSearchStringToSelection",
|
||||
"Finder:GetInitialSelection",
|
||||
"Finder:Highlight",
|
||||
"Finder:EnableSelection",
|
||||
"Finder:RemoveSelection",
|
||||
@ -153,6 +182,22 @@ RemoteFinderListener.prototype = {
|
||||
this._finder.caseSensitive = data.caseSensitive;
|
||||
break;
|
||||
|
||||
case "Finder:SetSearchStringToSelection": {
|
||||
let selection = this._finder.setSearchStringToSelection();
|
||||
this._global.sendAsyncMessage("Finder:CurrentSelectionResult",
|
||||
{ selection: selection,
|
||||
initial: false });
|
||||
break;
|
||||
}
|
||||
|
||||
case "Finder:GetInitialSelection": {
|
||||
let selection = this._finder.getActiveSelectionText();
|
||||
this._global.sendAsyncMessage("Finder:CurrentSelectionResult",
|
||||
{ selection: selection,
|
||||
initial: true });
|
||||
break;
|
||||
}
|
||||
|
||||
case "Finder:FastFind":
|
||||
this._finder.fastFind(data.searchString, data.linksOnly);
|
||||
break;
|
||||
|
@ -286,7 +286,7 @@ function getLocale() {
|
||||
* just the AsyncObjectCaller
|
||||
*/
|
||||
function AsyncObjectCaller(aObjects, aMethod, aListener) {
|
||||
this.objects = aObjects.slice(0);
|
||||
this.objects = [...aObjects];
|
||||
this.method = aMethod;
|
||||
this.listener = aListener;
|
||||
|
||||
@ -518,7 +518,8 @@ var AddonManagerInternal = {
|
||||
installListeners: [],
|
||||
addonListeners: [],
|
||||
typeListeners: [],
|
||||
providers: [],
|
||||
pendingProviders: new Set(),
|
||||
providers: new Set(),
|
||||
providerShutdowns: new Map(),
|
||||
types: {},
|
||||
startupChanges: {},
|
||||
@ -666,10 +667,20 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
logger.debug(`Starting provider: ${providerName(aProvider)}`);
|
||||
callProvider(aProvider, "startup", null, aAppChanged, aOldAppVersion, aOldPlatformVersion);
|
||||
if ('shutdown' in aProvider) {
|
||||
let name = providerName(aProvider);
|
||||
let AMProviderShutdown = () => {
|
||||
// If the provider has been unregistered, it will have been removed from
|
||||
// this.providers. If it hasn't been unregistered, then this is a normal
|
||||
// shutdown - and we move it to this.pendingProviders incase we're
|
||||
// running in a test that will start AddonManager again.
|
||||
if (this.providers.has(aProvider)) {
|
||||
this.providers.delete(aProvider);
|
||||
this.pendingProviders.add(aProvider);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.debug("Calling shutdown blocker for " + name);
|
||||
resolve(aProvider.shutdown());
|
||||
@ -683,6 +694,10 @@ var AddonManagerInternal = {
|
||||
this.providerShutdowns.set(aProvider, AMProviderShutdown);
|
||||
AddonManager.shutdown.addBlocker(name, AMProviderShutdown);
|
||||
}
|
||||
|
||||
this.pendingProviders.delete(aProvider);
|
||||
this.providers.add(aProvider);
|
||||
logger.debug(`Provider finished startup: ${providerName(aProvider)}`);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -805,6 +820,7 @@ var AddonManagerInternal = {
|
||||
|
||||
try {
|
||||
Components.utils.import(url, {});
|
||||
logger.debug(`Loaded provider scope for ${url}`);
|
||||
}
|
||||
catch (e) {
|
||||
AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
|
||||
@ -822,7 +838,7 @@ var AddonManagerInternal = {
|
||||
// Once we start calling providers we must allow all normal methods to work.
|
||||
gStarted = true;
|
||||
|
||||
for (let provider of this.providers) {
|
||||
for (let provider of this.pendingProviders) {
|
||||
this._startProvider(provider, appChanged, oldAppVersion, oldPlatformVersion);
|
||||
}
|
||||
|
||||
@ -839,6 +855,9 @@ var AddonManagerInternal = {
|
||||
logger.error("startup failed", e);
|
||||
AddonManagerPrivate.recordException("AMI", "startup failed", e);
|
||||
}
|
||||
|
||||
logger.debug("Completed startup sequence");
|
||||
this.callManagerListeners("onStartup");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -858,7 +877,7 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aTypes must be an array or null",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
this.providers.push(aProvider);
|
||||
this.pendingProviders.add(aProvider);
|
||||
|
||||
if (aTypes) {
|
||||
aTypes.forEach(function(aType) {
|
||||
@ -906,13 +925,11 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aProvider must be specified",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let pos = 0;
|
||||
while (pos < this.providers.length) {
|
||||
if (this.providers[pos] == aProvider)
|
||||
this.providers.splice(pos, 1);
|
||||
else
|
||||
pos++;
|
||||
}
|
||||
this.providers.delete(aProvider);
|
||||
// The test harness will unregister XPIProvider *after* shutdown, which is
|
||||
// after the provider will have been moved from providers to
|
||||
// pendingProviders.
|
||||
this.pendingProviders.delete(aProvider);
|
||||
|
||||
for (let type in this.types) {
|
||||
this.types[type].providers = this.types[type].providers.filter(function filterProvider(p) p != aProvider);
|
||||
@ -944,6 +961,39 @@ var AddonManagerInternal = {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark a provider as safe to access via AddonManager APIs, before its
|
||||
* startup has completed.
|
||||
*
|
||||
* Normally a provider isn't marked as safe until after its (synchronous)
|
||||
* startup() method has returned. Until a provider has been marked safe,
|
||||
* it won't be used by any of the AddonManager APIs. markProviderSafe()
|
||||
* allows a provider to mark itself as safe during its startup; this can be
|
||||
* useful if the provider wants to perform tasks that block startup, which
|
||||
* happen after its required initialization tasks and therefore when the
|
||||
* provider is in a safe state.
|
||||
*
|
||||
* @param aProvider Provider object to mark safe
|
||||
*/
|
||||
markProviderSafe: function AMI_markProviderSafe(aProvider) {
|
||||
if (!gStarted) {
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
}
|
||||
|
||||
if (!aProvider || typeof aProvider != "object") {
|
||||
throw Components.Exception("aProvider must be specified",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
if (!this.pendingProviders.has(aProvider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pendingProviders.delete(aProvider);
|
||||
this.providers.add(aProvider);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls a method on all registered providers if it exists and consumes any
|
||||
* thrown exception. Return values are ignored. Any parameters after the
|
||||
@ -960,7 +1010,7 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aMethod must be a non-empty string",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let providers = this.providers.slice(0);
|
||||
let providers = [...this.providers];
|
||||
for (let provider of providers) {
|
||||
try {
|
||||
if (aMethod in provider)
|
||||
@ -998,6 +1048,8 @@ var AddonManagerInternal = {
|
||||
*/
|
||||
shutdownManager: Task.async(function* () {
|
||||
logger.debug("shutdown");
|
||||
this.callManagerListeners("onShutdown");
|
||||
|
||||
gRepoShutdownState = "pending";
|
||||
gShutdownInProgress = true;
|
||||
// Clean up listeners
|
||||
@ -1560,7 +1612,23 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aType must be a non-empty string",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
this.callProviders("addonChanged", aID, aType, aPendingRestart);
|
||||
// Temporary hack until bug 520124 lands.
|
||||
// We can get here during synchronous startup, at which point it's
|
||||
// considered unsafe (and therefore disallowed by AddonManager.jsm) to
|
||||
// access providers that haven't been initialized yet. Since this is when
|
||||
// XPIProvider is starting up, XPIProvider can't access itself via APIs
|
||||
// going through AddonManager.jsm. Furthermore, LightweightThemeManager may
|
||||
// not be initialized until after XPIProvider is, and therefore would also
|
||||
// be unaccessible during XPIProvider startup. Thankfully, these are the
|
||||
// only two uses of this API, and we know it's safe to use this API with
|
||||
// both providers; so we have this hack to allow bypassing the normal
|
||||
// safetey guard.
|
||||
// The notifyAddonChanged/addonChanged API will be unneeded and therefore
|
||||
// removed by bug 520124, so this is a temporary quick'n'dirty hack.
|
||||
let providers = [...this.providers, ...this.pendingProviders];
|
||||
for (let provider of providers) {
|
||||
callProvider(provider, "addonChanged", null, aID, aType, aPendingRestart);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1671,7 +1739,7 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aBrowser must be a nsIDOMElement or null",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let providers = this.providers.slice(0);
|
||||
let providers = [...this.providers];
|
||||
for (let provider of providers) {
|
||||
if (callProvider(provider, "supportsMimetype", false, aMimetype)) {
|
||||
callProviderAsync(provider, "getInstallForURL",
|
||||
@ -1803,8 +1871,9 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aURI is not a nsIURI",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Try all providers
|
||||
let providers = this.providers.slice(0);
|
||||
let providers = [...this.providers];
|
||||
for (let provider of providers) {
|
||||
var id = callProvider(provider, "mapURIToAddonID", null, aURI);
|
||||
if (id !== null) {
|
||||
@ -1831,7 +1900,7 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aMimetype must be a non-empty string",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let providers = this.providers.slice(0);
|
||||
let providers = [...this.providers];
|
||||
for (let provider of providers) {
|
||||
if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
|
||||
callProvider(provider, "isInstallEnabled"))
|
||||
@ -1863,7 +1932,7 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aURI must be a nsIURI or null",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let providers = this.providers.slice(0);
|
||||
let providers = [...this.providers];
|
||||
for (let provider of providers) {
|
||||
if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
|
||||
callProvider(provider, "isInstallAllowed", null, aURI))
|
||||
@ -2412,6 +2481,10 @@ this.AddonManagerPrivate = {
|
||||
AddonManagerInternal.unregisterProvider(aProvider);
|
||||
},
|
||||
|
||||
markProviderSafe: function AMP_markProviderSafe(aProvider) {
|
||||
AddonManagerInternal.markProviderSafe(aProvider);
|
||||
},
|
||||
|
||||
backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
|
||||
return AddonManagerInternal.backgroundUpdateCheck();
|
||||
},
|
||||
@ -2718,6 +2791,10 @@ this.AddonManager = {
|
||||
},
|
||||
#endif
|
||||
|
||||
get isReady() {
|
||||
return gStartupComplete && !gShutdownInProgress;
|
||||
},
|
||||
|
||||
getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
|
||||
aHash, aName, aIcons,
|
||||
aVersion, aBrowser) {
|
||||
|
@ -2094,6 +2094,8 @@ this.XPIProvider = {
|
||||
// Changes to installed extensions may have changed which theme is selected
|
||||
this.applyThemeChange();
|
||||
|
||||
AddonManagerPrivate.markProviderSafe(this);
|
||||
|
||||
if (aAppChanged === undefined) {
|
||||
// For new profiles we will never need to show the add-on selection UI
|
||||
Services.prefs.setBoolPref(PREF_SHOWN_SELECTION_UI, true);
|
||||
|
@ -911,6 +911,7 @@ function getExpectedInstall(aAddon) {
|
||||
|
||||
const AddonListener = {
|
||||
onPropertyChanged: function(aAddon, aProperties) {
|
||||
do_print(`Got onPropertyChanged event for ${aAddon.id}`);
|
||||
let [event, properties] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onPropertyChanged", event);
|
||||
do_check_eq(aProperties.length, properties.length);
|
||||
@ -924,6 +925,7 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onEnabling: function(aAddon, aRequiresRestart) {
|
||||
do_print(`Got onEnabling event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onEnabling", event);
|
||||
do_check_eq(aRequiresRestart, expectedRestart);
|
||||
@ -934,6 +936,7 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onEnabled: function(aAddon) {
|
||||
do_print(`Got onEnabled event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onEnabled", event);
|
||||
do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
|
||||
@ -941,6 +944,7 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onDisabling: function(aAddon, aRequiresRestart) {
|
||||
do_print(`Got onDisabling event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onDisabling", event);
|
||||
do_check_eq(aRequiresRestart, expectedRestart);
|
||||
@ -951,6 +955,7 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onDisabled: function(aAddon) {
|
||||
do_print(`Got onDisabled event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onDisabled", event);
|
||||
do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
|
||||
@ -958,6 +963,7 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onInstalling: function(aAddon, aRequiresRestart) {
|
||||
do_print(`Got onInstalling event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onInstalling", event);
|
||||
do_check_eq(aRequiresRestart, expectedRestart);
|
||||
@ -967,12 +973,14 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onInstalled: function(aAddon) {
|
||||
do_print(`Got onInstalled event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onInstalled", event);
|
||||
return check_test_completed(arguments);
|
||||
},
|
||||
|
||||
onUninstalling: function(aAddon, aRequiresRestart) {
|
||||
do_print(`Got onUninstalling event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onUninstalling", event);
|
||||
do_check_eq(aRequiresRestart, expectedRestart);
|
||||
@ -982,12 +990,14 @@ const AddonListener = {
|
||||
},
|
||||
|
||||
onUninstalled: function(aAddon) {
|
||||
do_print(`Got onUninstalled event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onUninstalled", event);
|
||||
return check_test_completed(arguments);
|
||||
},
|
||||
|
||||
onOperationCancelled: function(aAddon) {
|
||||
do_print(`Got onOperationCancelled event for ${aAddon.id}`);
|
||||
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
||||
do_check_eq("onOperationCancelled", event);
|
||||
return check_test_completed(arguments);
|
||||
|
49
toolkit/mozapps/extensions/test/xpcshell/test_isReady.js
Normal file
49
toolkit/mozapps/extensions/test/xpcshell/test_isReady.js
Normal file
@ -0,0 +1,49 @@
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
equal(AddonManager.isReady, false, "isReady should be false before startup");
|
||||
|
||||
let gotStartupEvent = false;
|
||||
let gotShutdownEvent = false;
|
||||
let listener = {
|
||||
onStartup() {
|
||||
gotStartupEvent = true;
|
||||
},
|
||||
onShutdown() {
|
||||
gotShutdownEvent = true;
|
||||
},
|
||||
};
|
||||
AddonManager.addManagerListener(listener);
|
||||
|
||||
do_print("Starting manager...");
|
||||
startupManager();
|
||||
equal(AddonManager.isReady, true, "isReady should be true after startup");
|
||||
equal(gotStartupEvent, true, "Should have seen onStartup event after startup");
|
||||
equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown");
|
||||
|
||||
gotStartupEvent = false;
|
||||
gotShutdownEvent = false;
|
||||
|
||||
do_print("Shutting down manager...");
|
||||
let shutdownPromise = promiseShutdownManager();
|
||||
equal(AddonManager.isReady, false, "isReady should be false when shutdown commences");
|
||||
yield shutdownPromise;
|
||||
|
||||
equal(AddonManager.isReady, false, "isReady should be false after shutdown");
|
||||
equal(gotStartupEvent, false, "Should not have seen onStartup event after shutdown");
|
||||
equal(gotShutdownEvent, true, "Should have seen onShutdown event after shutdown");
|
||||
|
||||
AddonManager.addManagerListener(listener);
|
||||
gotStartupEvent = false;
|
||||
gotShutdownEvent = false;
|
||||
|
||||
do_print("Starting manager again...");
|
||||
startupManager();
|
||||
equal(AddonManager.isReady, true, "isReady should be true after repeat startup");
|
||||
equal(gotStartupEvent, true, "Should have seen onStartup event after repeat startup");
|
||||
equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown, following repeat startup");
|
||||
});
|
@ -0,0 +1,47 @@
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
let startupOrder = [];
|
||||
|
||||
function mockAddonProvider(name) {
|
||||
let mockProvider = {
|
||||
markSafe: false,
|
||||
apiAccessed: false,
|
||||
|
||||
startup() {
|
||||
if (this.markSafe)
|
||||
AddonManagerPrivate.markProviderSafe(this);
|
||||
|
||||
let uri = Services.io.newURI("beard://long", null, null);
|
||||
AddonManager.isInstallEnabled("made-up-mimetype");
|
||||
},
|
||||
supportsMimetype(mimetype) {
|
||||
this.apiAccessed = true;
|
||||
return false;
|
||||
},
|
||||
|
||||
get name() name,
|
||||
};
|
||||
|
||||
return mockProvider;
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* testMarkSafe() {
|
||||
do_print("Starting with provider normally");
|
||||
let provider = mockAddonProvider("Mock1");
|
||||
AddonManagerPrivate.registerProvider(provider);
|
||||
startupManager();
|
||||
ok(!provider.apiAccessed, "Provider API should not have been accessed");
|
||||
AddonManagerPrivate.unregisterProvider(provider);
|
||||
yield promiseShutdownManager();
|
||||
|
||||
do_print("Starting with provider that marks itself safe");
|
||||
provider.apiAccessed = false;
|
||||
provider.markSafe = true;
|
||||
AddonManagerPrivate.registerProvider(provider);
|
||||
startupManager();
|
||||
ok(provider.apiAccessed, "Provider API should have been accessed");
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
let shutdownOrder = [];
|
||||
|
||||
function mockAddonProvider(name) {
|
||||
let mockProvider = {
|
||||
hasShutdown: false,
|
||||
unsafeAccess: false,
|
||||
|
||||
shutdownCallback: null,
|
||||
|
||||
startup() { },
|
||||
shutdown() {
|
||||
this.hasShutdown = true;
|
||||
shutdownOrder.push(this.name);
|
||||
if (this.shutdownCallback)
|
||||
return this.shutdownCallback();
|
||||
},
|
||||
getAddonByID(id, callback) {
|
||||
if (this.hasShutdown) {
|
||||
unsafeAccess = true;
|
||||
}
|
||||
callback(null);
|
||||
},
|
||||
|
||||
get name() name,
|
||||
};
|
||||
|
||||
return mockProvider;
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* unsafeProviderShutdown() {
|
||||
let firstProvider = mockAddonProvider("Mock1");
|
||||
AddonManagerPrivate.registerProvider(firstProvider);
|
||||
let secondProvider = mockAddonProvider("Mock2");
|
||||
AddonManagerPrivate.registerProvider(secondProvider);
|
||||
|
||||
startupManager();
|
||||
|
||||
let shutdownPromise = null;
|
||||
yield new Promise(resolve => {
|
||||
secondProvider.shutdownCallback = function() {
|
||||
return new Promise(shutdownResolve => {
|
||||
AddonManager.getAddonByID("does-not-exist", () => {
|
||||
shutdownResolve();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
shutdownPromise = promiseShutdownManager();
|
||||
});
|
||||
yield shutdownPromise;
|
||||
|
||||
equal(shutdownOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have shutdown in expected order");
|
||||
ok(!firstProvider.unsafeAccess, "First registered mock provider should not have been accessed unsafely");
|
||||
});
|
@ -0,0 +1,53 @@
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
let startupOrder = [];
|
||||
|
||||
function mockAddonProvider(name) {
|
||||
let mockProvider = {
|
||||
hasStarted: false,
|
||||
unsafeAccess: false,
|
||||
|
||||
startupCallback: null,
|
||||
|
||||
startup() {
|
||||
this.hasStarted = true;
|
||||
startupOrder.push(this.name);
|
||||
if (this.startupCallback)
|
||||
this.startupCallback();
|
||||
},
|
||||
getAddonByID(id, callback) {
|
||||
if (!this.hasStarted) {
|
||||
unsafeAccess = true;
|
||||
}
|
||||
callback(null);
|
||||
},
|
||||
|
||||
get name() name,
|
||||
};
|
||||
|
||||
return mockProvider;
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* unsafeProviderStartup() {
|
||||
let secondProvider = null;
|
||||
|
||||
yield new Promise(resolve => {
|
||||
let firstProvider = mockAddonProvider("Mock1");
|
||||
firstProvider.startupCallback = function() {
|
||||
AddonManager.getAddonByID("does-not-exist", resolve);
|
||||
};
|
||||
AddonManagerPrivate.registerProvider(firstProvider);
|
||||
|
||||
secondProvider = mockAddonProvider("Mock2");
|
||||
AddonManagerPrivate.registerProvider(secondProvider);
|
||||
|
||||
startupManager();
|
||||
});
|
||||
|
||||
equal(startupOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have hasStarted in expected order");
|
||||
ok(!secondProvider.unsafeAccess, "Second registered mock provider should not have been accessed unsafely");
|
||||
});
|
@ -11,11 +11,15 @@ support-files =
|
||||
[test_addon_path_service.js]
|
||||
[test_asyncBlocklistLoad.js]
|
||||
[test_DeferredSave.js]
|
||||
[test_isReady.js]
|
||||
[test_metadata_update.js]
|
||||
[test_openh264.js]
|
||||
run-if = appname == "firefox"
|
||||
[test_pluginInfoURL.js]
|
||||
[test_provider_markSafe.js]
|
||||
[test_provider_shutdown.js]
|
||||
[test_provider_unsafe_access_shutdown.js]
|
||||
[test_provider_unsafe_access_startup.js]
|
||||
[test_shutdown.js]
|
||||
[test_XPIcancel.js]
|
||||
[test_XPIStates.js]
|
||||
|
Loading…
Reference in New Issue
Block a user