mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to central, a=merge
This commit is contained in:
commit
d3fb589ae4
@ -1882,6 +1882,12 @@ pref("browser.tabs.remote.autostart.1", false);
|
||||
pref("browser.tabs.remote.autostart.2", true);
|
||||
#endif
|
||||
|
||||
// For the about:tabcrashed page
|
||||
pref("browser.tabs.crashReporting.sendReport", true);
|
||||
pref("browser.tabs.crashReporting.includeURL", false);
|
||||
pref("browser.tabs.crashReporting.emailMe", false);
|
||||
pref("browser.tabs.crashReporting.email", "");
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
#ifndef MOZ_MULET
|
||||
pref("layers.async-pan-zoom.enabled", true);
|
||||
|
@ -3,27 +3,66 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function parseQueryString() {
|
||||
let url = document.documentURI;
|
||||
let queryString = url.replace(/^about:tabcrashed?e=tabcrashed/, "");
|
||||
let URL = document.documentURI;
|
||||
let queryString = URL.replace(/^about:tabcrashed?e=tabcrashed/, "");
|
||||
|
||||
let titleMatch = queryString.match(/d=([^&]*)/);
|
||||
return titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "";
|
||||
let URLMatch = queryString.match(/u=([^&]*)/);
|
||||
return {
|
||||
title: titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "",
|
||||
URL: URLMatch && URLMatch[1] ? decodeURIComponent(URLMatch[1]) : "",
|
||||
};
|
||||
}
|
||||
|
||||
document.title = parseQueryString();
|
||||
function displayUI() {
|
||||
if (!hasReport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
function shouldSendReport() {
|
||||
if (!document.documentElement.classList.contains("crashDumpAvailable"))
|
||||
return false;
|
||||
return document.getElementById("sendReport").checked;
|
||||
let sendCrashReport = document.getElementById("sendReport").checked;
|
||||
let container = document.getElementById("crash-reporter-container");
|
||||
container.hidden = !sendCrashReport;
|
||||
}
|
||||
|
||||
function hasReport() {
|
||||
return document.documentElement.classList.contains("crashDumpAvailable");
|
||||
}
|
||||
|
||||
function sendEvent(message) {
|
||||
let comments = "";
|
||||
let email = "";
|
||||
let URL = "";
|
||||
let sendCrashReport = false;
|
||||
let emailMe = false;
|
||||
let includeURL = false;
|
||||
|
||||
if (hasReport()) {
|
||||
sendCrashReport = document.getElementById("sendReport").checked;
|
||||
if (sendCrashReport) {
|
||||
comments = document.getElementById("comments").value.trim();
|
||||
|
||||
includeURL = document.getElementById("includeURL").checked;
|
||||
if (includeURL) {
|
||||
URL = parseQueryString().URL.trim();
|
||||
}
|
||||
|
||||
emailMe = document.getElementById("emailMe").checked;
|
||||
if (emailMe) {
|
||||
email = document.getElementById("email").value.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let event = new CustomEvent("AboutTabCrashedMessage", {
|
||||
bubbles: true,
|
||||
detail: {
|
||||
message,
|
||||
sendCrashReport: shouldSendReport(),
|
||||
sendCrashReport,
|
||||
comments,
|
||||
email,
|
||||
emailMe,
|
||||
includeURL,
|
||||
URL,
|
||||
},
|
||||
});
|
||||
|
||||
@ -42,6 +81,17 @@ function restoreAll() {
|
||||
sendEvent("restoreAll");
|
||||
}
|
||||
|
||||
document.title = parseQueryString().title;
|
||||
|
||||
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
|
||||
var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
|
||||
document.dispatchEvent(event);
|
||||
|
||||
addEventListener("DOMContentLoaded", function() {
|
||||
let sendReport = document.getElementById("sendReport");
|
||||
sendReport.addEventListener("click", function() {
|
||||
displayUI();
|
||||
});
|
||||
|
||||
displayUI();
|
||||
});
|
||||
|
@ -37,8 +37,22 @@
|
||||
<p>&tabCrashed.message;</p>
|
||||
|
||||
<div id="report-box">
|
||||
<input type="checkbox" id="sendReport" checked="checked"/>
|
||||
<input type="checkbox" id="sendReport"/>
|
||||
<label for="sendReport">&tabCrashed.sendReport;</label>
|
||||
<div id="crash-reporter-container" hidden="true">
|
||||
<p id="crash-reporter-title">&tabCrashed.crashReporter;</p>
|
||||
<textarea id="comments" placeholder="&tabCrashed.commentPlaceholder;" rows="4"></textarea>
|
||||
|
||||
<ul id="options">
|
||||
<li><input type="checkbox" id="includeURL"/>
|
||||
<label for="includeURL">&tabCrashed.includeURL;</label></li>
|
||||
|
||||
<li><input type="checkbox" id="emailMe"/>
|
||||
<label for="emailMe">&tabCrashed.emailMe;</label></li>
|
||||
</ul>
|
||||
|
||||
<input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p id="reportSent">&tabCrashed.reportSent;</p>
|
||||
|
@ -1166,7 +1166,15 @@ var gBrowserInit = {
|
||||
let browser = gBrowser.getBrowserForDocument(ownerDoc);
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (event.detail.sendCrashReport) {
|
||||
TabCrashReporter.submitCrashReport(browser);
|
||||
TabCrashReporter.submitCrashReport(browser, {
|
||||
comments: event.detail.comments,
|
||||
email: event.detail.email,
|
||||
emailMe: event.detail.emailMe,
|
||||
includeURL: event.detail.includeURL,
|
||||
URL: event.detail.URL,
|
||||
});
|
||||
} else {
|
||||
TabCrashReporter.dontSubmitCrashReport();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -5355,13 +5355,11 @@
|
||||
]]></handler>
|
||||
|
||||
<handler event="dblclick"><![CDATA[
|
||||
if (gBrowser.AppConstants.platform != "macosx") {
|
||||
// When the tabbar has an unified appearance with the titlebar
|
||||
// and menubar, a double-click in it should have the same behavior
|
||||
// as double-clicking the titlebar
|
||||
if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
|
||||
return;
|
||||
}
|
||||
// When the tabbar has an unified appearance with the titlebar
|
||||
// and menubar, a double-click in it should have the same behavior
|
||||
// as double-clicking the titlebar
|
||||
if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
|
||||
return;
|
||||
|
||||
if (event.button != 0 ||
|
||||
event.originalTarget.localName != "box")
|
||||
|
@ -525,3 +525,5 @@ support-files =
|
||||
readerModeArticle.html
|
||||
[browser_domFullscreen_fullscreenMode.js]
|
||||
[browser_menuButtonBadgeManager.js]
|
||||
[browser_aboutTabCrashed.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
|
335
browser/base/content/test/general/browser_aboutTabCrashed.js
Normal file
335
browser/base/content/test/general/browser_aboutTabCrashed.js
Normal file
@ -0,0 +1,335 @@
|
||||
"use strict";
|
||||
|
||||
let { TabCrashReporter } =
|
||||
Cu.import("resource:///modules/ContentCrashReporters.jsm");
|
||||
|
||||
const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
|
||||
const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
|
||||
const COMMENTS = "Here's my test comment!";
|
||||
const EMAIL = "foo@privacy.com";
|
||||
|
||||
/**
|
||||
* For an nsIPropertyBag, returns the value for a given
|
||||
* key.
|
||||
*
|
||||
* @param bag
|
||||
* The nsIPropertyBag to retrieve the value from
|
||||
* @param key
|
||||
* The key that we want to get the value for from the
|
||||
* bag
|
||||
* @returns The value corresponding to the key from the bag,
|
||||
* or null if the value could not be retrieved (for
|
||||
* example, if no value is set at that key).
|
||||
*/
|
||||
function getPropertyBagValue(bag, key) {
|
||||
try {
|
||||
let val = bag.getProperty(key);
|
||||
return val;
|
||||
} catch(e if e.result == Cr.NS_ERROR_FAILURE) {}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves once a crash report has
|
||||
* been submitted. This function will also test the crash
|
||||
* reports extra data to see if it matches expectedExtra.
|
||||
*
|
||||
* @param expectedExtra
|
||||
* An Object whose key-value pairs will be compared
|
||||
* against the key-value pairs in the extra data of the
|
||||
* crash report. A test failure will occur if there is
|
||||
* a mismatch.
|
||||
*
|
||||
* Note that this will only check the values that exist
|
||||
* in expectedExtra. It's possible that the crash report
|
||||
* will contain other extra information that is not
|
||||
* compared against.
|
||||
* @returns Promise
|
||||
*/
|
||||
function promiseCrashReport(expectedExtra) {
|
||||
return Task.spawn(function*() {
|
||||
info("Starting wait on crash-report-status");
|
||||
let [subject, data] =
|
||||
yield TestUtils.topicObserved("crash-report-status", (subject, data) => {
|
||||
return data == "success";
|
||||
});
|
||||
info("Topic observed!");
|
||||
|
||||
if (!(subject instanceof Ci.nsIPropertyBag2)) {
|
||||
throw new Error("Subject was not a Ci.nsIPropertyBag2");
|
||||
}
|
||||
|
||||
let remoteID = getPropertyBagValue(subject, "serverCrashID");
|
||||
if (!remoteID) {
|
||||
throw new Error("Report should have a server ID");
|
||||
}
|
||||
|
||||
let file = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(Services.crashmanager._submittedDumpsDir);
|
||||
file.append(remoteID + ".txt");
|
||||
if (!file.exists()) {
|
||||
throw new Error("Report should have been received by the server");
|
||||
}
|
||||
|
||||
file.remove(false);
|
||||
|
||||
let extra = getPropertyBagValue(subject, "extra");
|
||||
if (!(extra instanceof Ci.nsIPropertyBag2)) {
|
||||
throw new Error("extra was not a Ci.nsIPropertyBag2");
|
||||
}
|
||||
|
||||
info("Iterating crash report extra keys");
|
||||
let enumerator = extra.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let key = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
|
||||
let value = extra.getPropertyAsAString(key);
|
||||
if (key in expectedExtra) {
|
||||
is(value, expectedExtra[key],
|
||||
`Crash report had the right extra value for ${key}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the browser to send crash reports to the local crash report
|
||||
* testing server.
|
||||
*/
|
||||
add_task(function* setup() {
|
||||
// The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash
|
||||
// reports. This test needs them enabled. The test also needs a mock
|
||||
// report server, and fortunately one is already set up by toolkit/
|
||||
// crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL,
|
||||
// which CrashSubmit.jsm uses as a server override.
|
||||
let env = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Components.interfaces.nsIEnvironment);
|
||||
let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
|
||||
let serverUrl = env.get("MOZ_CRASHREPORTER_URL");
|
||||
env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
|
||||
env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
|
||||
env.set("MOZ_CRASHREPORTER_URL", serverUrl);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This function returns a Promise that resolves once the following
|
||||
* actions have taken place:
|
||||
*
|
||||
* 1) A new tab is opened up at PAGE
|
||||
* 2) The tab is crashed
|
||||
* 3) The about:tabcrashed page's fields are set in accordance with
|
||||
* fieldValues
|
||||
* 4) The tab is restored
|
||||
* 5) A crash report is received from the testing server
|
||||
* 6) Any tab crash prefs that were overwritten are reset
|
||||
*
|
||||
* @param fieldValues
|
||||
* An Object describing how to set the about:tabcrashed
|
||||
* fields. The following properties are accepted:
|
||||
*
|
||||
* comments (String)
|
||||
* The comments to put in the comment textarea
|
||||
* email (String)
|
||||
* The email address to put in the email address input
|
||||
* emailMe (bool)
|
||||
* The checked value of the "Email me" checkbox
|
||||
* includeURL (bool)
|
||||
* The checked value of the "Include URL" checkbox
|
||||
*
|
||||
* If any of these fields are missing, the defaults from
|
||||
* the user preferences are used.
|
||||
* @param expectedExtra
|
||||
* An Object describing the expected values that the submitted
|
||||
* crash report's extra data should contain.
|
||||
* @returns Promise
|
||||
*/
|
||||
function crashTabTestHelper(fieldValues, expectedExtra) {
|
||||
return BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, function*(browser) {
|
||||
let prefs = TabCrashReporter.prefs;
|
||||
let originalSendReport = prefs.getBoolPref("sendReport");
|
||||
let originalEmailMe = prefs.getBoolPref("emailMe");
|
||||
let originalIncludeURL = prefs.getBoolPref("includeURL");
|
||||
let originalEmail = prefs.getCharPref("email");
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
let doc = browser.contentDocument;
|
||||
|
||||
// Since about:tabcrashed will run in the parent process, we can safely
|
||||
// manipulate its DOM nodes directly
|
||||
let comments = doc.getElementById("comments");
|
||||
let email = doc.getElementById("email");
|
||||
let emailMe = doc.getElementById("emailMe");
|
||||
let includeURL = doc.getElementById("includeURL");
|
||||
|
||||
if (fieldValues.hasOwnProperty("comments")) {
|
||||
comments.value = fieldValues.comments;
|
||||
}
|
||||
|
||||
if (fieldValues.hasOwnProperty("email")) {
|
||||
email.value = fieldValues.email;
|
||||
}
|
||||
|
||||
if (fieldValues.hasOwnProperty("emailMe")) {
|
||||
emailMe.checked = fieldValues.emailMe;
|
||||
}
|
||||
|
||||
if (fieldValues.hasOwnProperty("includeURL")) {
|
||||
includeURL.checked = fieldValues.includeURL;
|
||||
}
|
||||
|
||||
let crashReport = promiseCrashReport(expectedExtra);
|
||||
let restoreTab = browser.contentDocument.getElementById("restoreTab");
|
||||
restoreTab.click();
|
||||
yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
|
||||
yield crashReport;
|
||||
|
||||
// Submitting the crash report may have set some prefs regarding how to
|
||||
// send tab crash reports. Let's reset them for the next test.
|
||||
prefs.setBoolPref("sendReport", originalSendReport);
|
||||
prefs.setBoolPref("emailMe", originalEmailMe);
|
||||
prefs.setBoolPref("includeURL", originalIncludeURL);
|
||||
prefs.setCharPref("email", originalEmail);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests what we send with the crash report by default. By default, we do not
|
||||
* send any comments, the URL of the crashing page, or the email address of
|
||||
* the user.
|
||||
*/
|
||||
add_task(function* test_default() {
|
||||
yield crashTabTestHelper({}, {
|
||||
"Comments": "",
|
||||
"URL": "",
|
||||
"Email": "",
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test just sending a comment.
|
||||
*/
|
||||
add_task(function* test_just_a_comment() {
|
||||
yield crashTabTestHelper({
|
||||
comments: COMMENTS,
|
||||
}, {
|
||||
"Comments": COMMENTS,
|
||||
"URL": "",
|
||||
"Email": "",
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that we don't send email if emailMe is unchecked
|
||||
*/
|
||||
add_task(function* test_no_email() {
|
||||
yield crashTabTestHelper({
|
||||
email: EMAIL,
|
||||
emailMe: false,
|
||||
}, {
|
||||
"Comments": "",
|
||||
"URL": "",
|
||||
"Email": "",
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that we can send an email address if emailMe is checked
|
||||
*/
|
||||
add_task(function* test_yes_email() {
|
||||
yield crashTabTestHelper({
|
||||
email: EMAIL,
|
||||
emailMe: true,
|
||||
}, {
|
||||
"Comments": "",
|
||||
"URL": "",
|
||||
"Email": EMAIL,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that we will send the URL of the page if includeURL is checked.
|
||||
*/
|
||||
add_task(function* test_send_URL() {
|
||||
yield crashTabTestHelper({
|
||||
includeURL: true,
|
||||
}, {
|
||||
"Comments": "",
|
||||
"URL": PAGE,
|
||||
"Email": "",
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that we can send comments, the email address, and the URL
|
||||
*/
|
||||
add_task(function* test_send_all() {
|
||||
yield crashTabTestHelper({
|
||||
includeURL: true,
|
||||
emailMe: true,
|
||||
email: EMAIL,
|
||||
comments: COMMENTS,
|
||||
}, {
|
||||
"Comments": COMMENTS,
|
||||
"URL": PAGE,
|
||||
"Email": EMAIL,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that if we have an email address stored in prefs, and we decide
|
||||
* not to submit the email address in the next crash report, that we
|
||||
* clear the email address.
|
||||
*/
|
||||
add_task(function* test_clear_email() {
|
||||
return BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, function*(browser) {
|
||||
let prefs = TabCrashReporter.prefs;
|
||||
let originalSendReport = prefs.getBoolPref("sendReport");
|
||||
let originalEmailMe = prefs.getBoolPref("emailMe");
|
||||
let originalIncludeURL = prefs.getBoolPref("includeURL");
|
||||
let originalEmail = prefs.getCharPref("email");
|
||||
|
||||
// Pretend that we stored an email address from the previous
|
||||
// crash
|
||||
prefs.setCharPref("email", EMAIL);
|
||||
prefs.setBoolPref("emailMe", true);
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
let doc = browser.contentDocument;
|
||||
|
||||
// Since about:tabcrashed will run in the parent process, we can safely
|
||||
// manipulate its DOM nodes directly
|
||||
let emailMe = doc.getElementById("emailMe");
|
||||
emailMe.checked = false;
|
||||
|
||||
let crashReport = promiseCrashReport({
|
||||
Email: "",
|
||||
});
|
||||
|
||||
let restoreTab = browser.contentDocument.getElementById("restoreTab");
|
||||
restoreTab.click();
|
||||
yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
|
||||
yield crashReport;
|
||||
|
||||
is(prefs.getCharPref("email"), "", "No email address should be stored");
|
||||
|
||||
// Submitting the crash report may have set some prefs regarding how to
|
||||
// send tab crash reports. Let's reset them for the next test.
|
||||
prefs.setBoolPref("sendReport", originalSendReport);
|
||||
prefs.setBoolPref("emailMe", originalEmailMe);
|
||||
prefs.setBoolPref("includeURL", originalIncludeURL);
|
||||
prefs.setCharPref("email", originalEmail);
|
||||
});
|
||||
});
|
@ -120,10 +120,12 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
|
||||
_handleShareTabs: function() {
|
||||
this._startScreenShare("browser");
|
||||
this.hideDropdownMenu();
|
||||
},
|
||||
|
||||
_handleShareWindows: function() {
|
||||
this._startScreenShare("window");
|
||||
this.hideDropdownMenu();
|
||||
},
|
||||
|
||||
_getTitle: function() {
|
||||
|
@ -120,10 +120,12 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
|
||||
_handleShareTabs: function() {
|
||||
this._startScreenShare("browser");
|
||||
this.hideDropdownMenu();
|
||||
},
|
||||
|
||||
_handleShareWindows: function() {
|
||||
this._startScreenShare("window");
|
||||
this.hideDropdownMenu();
|
||||
},
|
||||
|
||||
_getTitle: function() {
|
||||
|
@ -176,6 +176,22 @@ describe("loop.shared.views", function() {
|
||||
new sharedActions.StartScreenShare({ type: "browser" }));
|
||||
});
|
||||
|
||||
it("should close the dropdown on 'browser' option click", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
|
||||
sandbox.stub(comp, "hideDropdownMenu");
|
||||
|
||||
TestUtils.Simulate.click(comp.getDOMNode().querySelector(
|
||||
".screen-share-menu > li"));
|
||||
|
||||
sinon.assert.calledOnce(comp.hideDropdownMenu);
|
||||
});
|
||||
|
||||
it("should dispatch a 'window' StartScreenShare action on option click",
|
||||
function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
@ -193,6 +209,22 @@ describe("loop.shared.views", function() {
|
||||
new sharedActions.StartScreenShare({ type: "window" }));
|
||||
});
|
||||
|
||||
it("should close the dropdown on 'window' option click", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
|
||||
sandbox.stub(comp, "hideDropdownMenu");
|
||||
|
||||
TestUtils.Simulate.click(comp.getDOMNode().querySelector(
|
||||
".screen-share-menu > li:last-child"));
|
||||
|
||||
sinon.assert.calledOnce(comp.hideDropdownMenu);
|
||||
});
|
||||
|
||||
it("should have the 'window' option enabled", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
|
@ -69,7 +69,7 @@ add_task(function* test_crash() {
|
||||
// the content process. The "crash" message makes it first so that we don't
|
||||
// get a chance to process the flush. The TabStateFlusher however should be
|
||||
// notified so that the flush still completes.
|
||||
let promise1 = crashBrowser(browser);
|
||||
let promise1 = BrowserTestUtils.crashBrowser(browser);
|
||||
let promise2 = TabStateFlusher.flush(browser);
|
||||
yield Promise.all([promise1, promise2]);
|
||||
|
||||
|
@ -117,7 +117,7 @@ add_task(function test_crash_page_not_in_history() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
// Check the tab state and make sure the tab crashed page isn't
|
||||
// mentioned.
|
||||
@ -146,7 +146,7 @@ add_task(function test_revived_history_from_remote() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
// Browse to a new site that will cause the browser to
|
||||
// become remote again.
|
||||
@ -185,7 +185,7 @@ add_task(function test_revived_history_from_non_remote() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
// Browse to a new site that will not cause the browser to
|
||||
// become remote again.
|
||||
@ -235,7 +235,7 @@ add_task(function test_revive_tab_from_session_store() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
|
||||
|
||||
// Use SessionStore to revive the tab
|
||||
@ -286,7 +286,7 @@ add_task(function test_revive_all_tabs_from_session_store() {
|
||||
yield TabStateFlusher.flush(browser2);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
|
||||
|
||||
// Use SessionStore to revive all the tabs
|
||||
@ -331,7 +331,7 @@ add_task(function test_close_tab_after_crash() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
let promise = promiseEvent(gBrowser.tabContainer, "TabClose");
|
||||
|
||||
@ -359,7 +359,7 @@ add_task(function* test_hide_restore_all_button() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
let doc = browser.contentDocument;
|
||||
let restoreAllButton = doc.getElementById("restoreAll");
|
||||
@ -375,7 +375,7 @@ add_task(function* test_hide_restore_all_button() {
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
doc = browser.contentDocument;
|
||||
restoreAllButton = doc.getElementById("restoreAll");
|
||||
@ -405,7 +405,7 @@ add_task(function* test_aboutcrashedtabzoom() {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// Crash the tab
|
||||
yield crashBrowser(browser);
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
ok(ZoomManager.getZoomForBrowser(browser) === 1, "zoom should have reset on crash");
|
||||
|
||||
|
@ -538,112 +538,6 @@ function promiseRemoveTab(tab) {
|
||||
return BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves once a remote <xul:browser> has experienced
|
||||
* a crash. Also does the job of cleaning up the minidump of the crash.
|
||||
*
|
||||
* @param browser
|
||||
* The <xul:browser> that will crash
|
||||
* @return Promise
|
||||
*/
|
||||
function crashBrowser(browser) {
|
||||
/**
|
||||
* Returns the directory where crash dumps are stored.
|
||||
*
|
||||
* @return nsIFile
|
||||
*/
|
||||
function getMinidumpDirectory() {
|
||||
let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
|
||||
dir.append("minidumps");
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from a directory. This is a no-op if the file does not
|
||||
* exist.
|
||||
*
|
||||
* @param directory
|
||||
* The nsIFile representing the directory to remove from.
|
||||
* @param filename
|
||||
* A string for the file to remove from the directory.
|
||||
*/
|
||||
function removeFile(directory, filename) {
|
||||
let file = directory.clone();
|
||||
file.append(filename);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
// This frame script is injected into the remote browser, and used to
|
||||
// intentionally crash the tab. We crash by using js-ctypes and dereferencing
|
||||
// a bad pointer. The crash should happen immediately upon loading this
|
||||
// frame script.
|
||||
let frame_script = () => {
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
let dies = function() {
|
||||
privateNoteIntentionalCrash();
|
||||
let zero = new ctypes.intptr_t(8);
|
||||
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
|
||||
badptr.contents
|
||||
};
|
||||
|
||||
dump("Et tu, Brute?");
|
||||
dies();
|
||||
}
|
||||
|
||||
let crashCleanupPromise = new Promise((resolve, reject) => {
|
||||
let observer = (subject, topic, data) => {
|
||||
is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
|
||||
ok(subject instanceof Ci.nsIPropertyBag2,
|
||||
'Subject implements nsIPropertyBag2.');
|
||||
// we might see this called as the process terminates due to previous tests.
|
||||
// We are only looking for "abnormal" exits...
|
||||
if (!subject.hasKey("abnormal")) {
|
||||
info("This is a normal termination and isn't the one we are looking for...");
|
||||
return;
|
||||
}
|
||||
|
||||
let dumpID;
|
||||
if ('nsICrashReporter' in Ci) {
|
||||
dumpID = subject.getPropertyAsAString('dumpID');
|
||||
ok(dumpID, "dumpID is present and not an empty string");
|
||||
}
|
||||
|
||||
if (dumpID) {
|
||||
let minidumpDirectory = getMinidumpDirectory();
|
||||
removeFile(minidumpDirectory, dumpID + '.dmp');
|
||||
removeFile(minidumpDirectory, dumpID + '.extra');
|
||||
}
|
||||
|
||||
Services.obs.removeObserver(observer, 'ipc:content-shutdown');
|
||||
info("Crash cleaned up");
|
||||
resolve();
|
||||
};
|
||||
|
||||
Services.obs.addObserver(observer, 'ipc:content-shutdown');
|
||||
});
|
||||
|
||||
let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
|
||||
browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
|
||||
browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
|
||||
info("about:tabcrashed loaded");
|
||||
resolve();
|
||||
}, false, true);
|
||||
});
|
||||
|
||||
// This frame script will crash the remote browser as soon as it is
|
||||
// evaluated.
|
||||
let mm = browser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
|
||||
return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]).then(() => {
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
is(tab.getAttribute("crashed"), "true", "Tab should be marked as crashed");
|
||||
});
|
||||
}
|
||||
|
||||
// Write DOMSessionStorage data to the given browser.
|
||||
function modifySessionStorage(browser, data, options = {}) {
|
||||
return ContentTask.spawn(browser, [data, options], function* ([data, options]) {
|
||||
|
@ -5,6 +5,11 @@
|
||||
<!ENTITY tabCrashed.header "Bad news first: This tab has crashed">
|
||||
<!ENTITY tabCrashed.message "Now for the good news: You can just close this tab, restore it or restore all your crashed tabs.">
|
||||
<!ENTITY tabCrashed.sendReport "Submit a crash report to help prevent more bad news">
|
||||
<!ENTITY tabCrashed.includeURL "Include the address of the page I was on">
|
||||
<!ENTITY tabCrashed.commentPlaceholder "Add a comment (comments are publicly visible)">
|
||||
<!ENTITY tabCrashed.emailPlaceholder "Enter your email address here">
|
||||
<!ENTITY tabCrashed.emailMe "Email me when more information is available">
|
||||
<!ENTITY tabCrashed.crashReporter "Mozilla Crash Reporter">
|
||||
<!ENTITY tabCrashed.reportSent "Crash report already submitted; thank you for helping make &brandShortName; better!">
|
||||
<!ENTITY tabCrashed.closeTab "Close This Tab">
|
||||
<!ENTITY tabCrashed.restoreTab "Restore This Tab">
|
||||
|
@ -17,6 +17,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
|
||||
"resource://gre/modules/CrashSubmit.jsm");
|
||||
|
||||
this.TabCrashReporter = {
|
||||
get prefs() {
|
||||
delete this.prefs;
|
||||
return this.prefs = Services.prefs.getBranch("browser.tabs.crashReporting.");
|
||||
},
|
||||
|
||||
init: function () {
|
||||
if (this.initialized)
|
||||
return;
|
||||
@ -47,21 +52,66 @@ this.TabCrashReporter = {
|
||||
if (!browser)
|
||||
return;
|
||||
|
||||
this.browserMap.set(browser, aSubject.childID);
|
||||
this.browserMap.set(browser.permanentKey, aSubject.childID);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
submitCrashReport: function (aBrowser) {
|
||||
let childID = this.browserMap.get(aBrowser);
|
||||
/**
|
||||
* Submits a crash report from about:tabcrashed
|
||||
*
|
||||
* @param aBrowser
|
||||
* The <xul:browser> that the report was sent from.
|
||||
* @param aFormData
|
||||
* An Object with the following properties:
|
||||
*
|
||||
* includeURL (bool):
|
||||
* Whether to include the URL that the user was on
|
||||
* in the crashed tab before the crash occurred.
|
||||
* URL (String)
|
||||
* The URL that the user was on in the crashed tab
|
||||
* before the crash occurred.
|
||||
* emailMe (bool):
|
||||
* Whether or not to include the user's email address
|
||||
* in the crash report.
|
||||
* email (String):
|
||||
* The email address of the user.
|
||||
* comments (String):
|
||||
* Any additional comments from the user.
|
||||
*
|
||||
* Note that it is expected that all properties are set,
|
||||
* even if they are empty.
|
||||
*/
|
||||
submitCrashReport: function (aBrowser, aFormData) {
|
||||
let childID = this.browserMap.get(aBrowser.permanentKey);
|
||||
let dumpID = this.childMap.get(childID);
|
||||
if (!dumpID)
|
||||
return
|
||||
|
||||
if (CrashSubmit.submit(dumpID, { recordSubmission: true })) {
|
||||
this.childMap.set(childID, null); // Avoid resubmission.
|
||||
this.removeSubmitCheckboxesForSameCrash(childID);
|
||||
CrashSubmit.submit(dumpID, {
|
||||
recordSubmission: true,
|
||||
extraExtraKeyVals: {
|
||||
Comments: aFormData.comments,
|
||||
Email: aFormData.email,
|
||||
URL: aFormData.URL,
|
||||
},
|
||||
}).then(null, Cu.reportError);
|
||||
|
||||
this.prefs.setBoolPref("sendReport", true);
|
||||
this.prefs.setBoolPref("includeURL", aFormData.includeURL);
|
||||
this.prefs.setBoolPref("emailMe", aFormData.emailMe);
|
||||
if (aFormData.emailMe) {
|
||||
this.prefs.setCharPref("email", aFormData.email);
|
||||
} else {
|
||||
this.prefs.setCharPref("email", "");
|
||||
}
|
||||
|
||||
this.childMap.set(childID, null); // Avoid resubmission.
|
||||
this.removeSubmitCheckboxesForSameCrash(childID);
|
||||
},
|
||||
|
||||
dontSubmitCrashReport: function() {
|
||||
this.prefs.setBoolPref("sendReport", false);
|
||||
},
|
||||
|
||||
removeSubmitCheckboxesForSameCrash: function(childID) {
|
||||
@ -79,8 +129,8 @@ this.TabCrashReporter = {
|
||||
if (!doc.documentURI.startsWith("about:tabcrashed"))
|
||||
continue;
|
||||
|
||||
if (this.browserMap.get(browser) == childID) {
|
||||
this.browserMap.delete(browser);
|
||||
if (this.browserMap.get(browser.permanentKey) == childID) {
|
||||
this.browserMap.delete(browser.permanentKey);
|
||||
browser.contentDocument.documentElement.classList.remove("crashDumpAvailable");
|
||||
browser.contentDocument.documentElement.classList.add("crashDumpSubmitted");
|
||||
}
|
||||
@ -97,11 +147,27 @@ this.TabCrashReporter = {
|
||||
if (!this.childMap)
|
||||
return;
|
||||
|
||||
let dumpID = this.childMap.get(this.browserMap.get(aBrowser));
|
||||
let dumpID = this.childMap.get(this.browserMap.get(aBrowser.permanentKey));
|
||||
if (!dumpID)
|
||||
return;
|
||||
|
||||
aBrowser.contentDocument.documentElement.classList.add("crashDumpAvailable");
|
||||
let doc = aBrowser.contentDocument;
|
||||
|
||||
doc.documentElement.classList.add("crashDumpAvailable");
|
||||
|
||||
let sendReport = this.prefs.getBoolPref("sendReport");
|
||||
doc.getElementById("sendReport").checked = sendReport;
|
||||
|
||||
let includeURL = this.prefs.getBoolPref("includeURL");
|
||||
doc.getElementById("includeURL").checked = includeURL;
|
||||
|
||||
let emailMe = this.prefs.getBoolPref("emailMe");
|
||||
doc.getElementById("emailMe").checked = emailMe;
|
||||
|
||||
if (emailMe) {
|
||||
let email = this.prefs.getCharPref("email", "");
|
||||
doc.getElementById("email").value = email;
|
||||
}
|
||||
},
|
||||
|
||||
hideRestoreAllButton: function (aBrowser) {
|
||||
|
@ -98,12 +98,6 @@
|
||||
-moz-appearance: -moz-window-titlebar;
|
||||
}
|
||||
|
||||
@media (-moz-mac-yosemite-theme) {
|
||||
#main-window:not(:-moz-lwtheme) > #titlebar {
|
||||
-moz-appearance: -moz-mac-vibrancy-light;
|
||||
}
|
||||
}
|
||||
|
||||
#main-window:not([tabsintitlebar]) > #titlebar {
|
||||
height: 22px; /* The native titlebar on OS X is 22px tall. */
|
||||
}
|
||||
|
@ -9,3 +9,34 @@
|
||||
#reportSent {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#crash-reporter-container {
|
||||
width: 80%;
|
||||
background-color: var(--in-content-box-background-hover);
|
||||
margin: 24px 0;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--in-content-box-border-color);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#crash-reporter-title {
|
||||
font-weight: bold;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
#options {
|
||||
list-style: none;
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
#options > li {
|
||||
margin: 14px 0 0 0;
|
||||
}
|
||||
|
@ -589,6 +589,7 @@ description > html|a {
|
||||
}
|
||||
|
||||
.fxaAccountBoxButtons > button {
|
||||
text-align: center;
|
||||
padding-left: 11px;
|
||||
padding-right: 11px;
|
||||
margin: 0;
|
||||
|
@ -2013,7 +2013,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
}
|
||||
|
||||
:root {
|
||||
--tab-toolbar-navbar-overlap: 0;
|
||||
--tab-toolbar-navbar-overlap: 0px;
|
||||
}
|
||||
|
||||
#nav-bar {
|
||||
|
@ -223,9 +223,7 @@ menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
|
||||
}
|
||||
|
||||
#BMB_bookmarksPopup menupopup[placespopup=true] > hbox {
|
||||
/* After fixing of bug 1194480 the box-shadow can be removed again */
|
||||
/* box-shadow: none; */
|
||||
box-shadow: 0 0 4px rgba(0,0,0,0.02);
|
||||
box-shadow: none;
|
||||
background: -moz-field;
|
||||
border: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
@ -567,6 +567,8 @@ skip-if = e10s && debug
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_watch-expressions-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_worker-window.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_WorkerActor.attach.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_WorkerActor.attachThread.js]
|
||||
|
@ -0,0 +1,43 @@
|
||||
// Check to make sure that a worker can be attached to a toolbox
|
||||
// directly, and that the toolbox has expected properties.
|
||||
|
||||
var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
|
||||
var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
|
||||
|
||||
add_task(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let tab = yield addTab(TAB_URL);
|
||||
let { tabs } = yield listTabs(client);
|
||||
let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
|
||||
|
||||
yield listWorkers(tabClient);
|
||||
yield createWorkerInTab(tab, WORKER_URL);
|
||||
|
||||
let { workers } = yield listWorkers(tabClient);
|
||||
let [, workerClient] = yield attachWorker(tabClient,
|
||||
findWorker(workers, WORKER_URL));
|
||||
|
||||
let toolbox = yield gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
|
||||
"jsdebugger",
|
||||
Toolbox.HostType.WINDOW);
|
||||
|
||||
is(toolbox._host.type, "window", "correct host");
|
||||
ok(toolbox._host._window.document.title.contains(WORKER_URL),
|
||||
"worker URL in host title");
|
||||
|
||||
let toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
|
||||
let activeTools = [...toolTabs].map(tab=>tab.getAttribute("toolid"));
|
||||
|
||||
is(activeTools.join(","), "webconsole,jsdebugger,scratchpad,options",
|
||||
"Correct set of tools supported by worker");
|
||||
|
||||
yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
|
||||
terminateWorkerInTab(tab, WORKER_URL);
|
||||
yield waitForWorkerClose(workerClient);
|
||||
yield close(client);
|
||||
});
|
@ -856,6 +856,10 @@ WorkerTarget.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
get url() {
|
||||
return this._workerClient.url;
|
||||
},
|
||||
|
||||
get form() {
|
||||
return {
|
||||
from: this._workerClient.actor,
|
||||
|
@ -1493,10 +1493,8 @@ Toolbox.prototype = {
|
||||
toolName = toolboxStrings("toolbox.defaultTitle");
|
||||
}
|
||||
let title = toolboxStrings("toolbox.titleTemplate",
|
||||
toolName,
|
||||
this.target.isAddon ?
|
||||
this.target.name :
|
||||
this.target.url || this.target.name);
|
||||
toolName, this.target.name ||
|
||||
this.target.url);
|
||||
this._host.setTitle(title);
|
||||
},
|
||||
|
||||
|
@ -26,6 +26,7 @@ const {
|
||||
throttle
|
||||
} = require("devtools/client/styleinspector/utils");
|
||||
const {
|
||||
escapeCSSComment,
|
||||
parseDeclarations,
|
||||
parseSingleValue,
|
||||
parsePseudoClassesAndAttributes,
|
||||
@ -1117,7 +1118,7 @@ TextProperty.prototype = {
|
||||
|
||||
// Comment out property declarations that are not enabled
|
||||
if (!this.enabled) {
|
||||
declaration = "/* " + declaration + " */";
|
||||
declaration = "/* " + escapeCSSComment(declaration) + " */";
|
||||
}
|
||||
|
||||
return declaration;
|
||||
|
@ -103,6 +103,7 @@ add_task(function*() {
|
||||
"\tbackground-color: #00F;[\\r\\n]+" +
|
||||
"\tfont-size: 12px;[\\r\\n]+" +
|
||||
"\tborder-color: #00F !important;[\\r\\n]+" +
|
||||
"\t--var: \"\\*/\";[\\r\\n]+" +
|
||||
"}",
|
||||
hidden: {
|
||||
copyLocation: true,
|
||||
@ -144,7 +145,7 @@ add_task(function*() {
|
||||
},
|
||||
{
|
||||
setup: function*() {
|
||||
yield disableProperty(view);
|
||||
yield disableProperty(view, 0);
|
||||
},
|
||||
desc: "Test Copy Rule with Disabled Property",
|
||||
node: ruleEditor.rule.textProps[2].editor.nameSpan,
|
||||
@ -154,6 +155,30 @@ add_task(function*() {
|
||||
"\tbackground-color: #00F;[\\r\\n]+" +
|
||||
"\tfont-size: 12px;[\\r\\n]+" +
|
||||
"\tborder-color: #00F !important;[\\r\\n]+" +
|
||||
"\t--var: \"\\*/\";[\\r\\n]+" +
|
||||
"}",
|
||||
hidden: {
|
||||
copyLocation: true,
|
||||
copyPropertyDeclaration: false,
|
||||
copyPropertyName: false,
|
||||
copyPropertyValue: true,
|
||||
copySelector: true,
|
||||
copyRule: false
|
||||
}
|
||||
},
|
||||
{
|
||||
setup: function*() {
|
||||
yield disableProperty(view, 4);
|
||||
},
|
||||
desc: "Test Copy Rule with Disabled Property with Comment",
|
||||
node: ruleEditor.rule.textProps[2].editor.nameSpan,
|
||||
menuItem: contextmenu.menuitemCopyRule,
|
||||
expectedPattern: "#testid {[\\r\\n]+" +
|
||||
"\t\/\\* color: #F00; \\*\/[\\r\\n]+" +
|
||||
"\tbackground-color: #00F;[\\r\\n]+" +
|
||||
"\tfont-size: 12px;[\\r\\n]+" +
|
||||
"\tborder-color: #00F !important;[\\r\\n]+" +
|
||||
"\t/\\* --var: \"\\*\\\\\/\"; \\*\/[\\r\\n]+" +
|
||||
"}",
|
||||
hidden: {
|
||||
copyLocation: true,
|
||||
@ -241,9 +266,9 @@ function* checkCopyStyle(view, node, menuItem, expectedPattern, hidden) {
|
||||
view._contextmenu._menupopup.hidePopup();
|
||||
}
|
||||
|
||||
function* disableProperty(view) {
|
||||
function* disableProperty(view, index) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
let propEditor = ruleEditor.rule.textProps[index].editor;
|
||||
|
||||
info("Disabling a property");
|
||||
propEditor.enable.click();
|
||||
|
@ -7,4 +7,5 @@ html, body, #testid {
|
||||
background-color: #00F;
|
||||
font-size: 12px;
|
||||
border-color: #00F !important;
|
||||
--var: "*/";
|
||||
}
|
||||
|
@ -2,14 +2,19 @@
|
||||
<!-- 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/. -->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<!-- Ignore UseCompoundDrawables because they caused a regression in bug 1208790. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="UseCompoundDrawables"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingRight="?android:attr/scrollbarSize">
|
||||
|
||||
<TextView android:id="@+id/label_search_hint"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:text="@string/pref_search_hint"
|
||||
android:layout_marginTop="5dip"
|
||||
android:layout_marginBottom="6dip"
|
||||
@ -17,8 +22,13 @@
|
||||
android:layout_marginRight="6dip"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingRight="8dip"
|
||||
android:drawableRight="@drawable/tip_addsearch"
|
||||
android:drawablePadding="6dp"/>
|
||||
android:paddingRight="6dip"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</FrameLayout>
|
||||
<ImageView android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingTop="12dip"
|
||||
android:src="@drawable/tip_addsearch"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -11,7 +11,6 @@ import org.mozilla.gecko.GeckoAppShell;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.util.Log;
|
||||
|
||||
public class Allocator {
|
||||
@ -46,6 +45,8 @@ public class Allocator {
|
||||
|
||||
SharedPreferences mPrefs;
|
||||
|
||||
@SuppressWarnings("deprecation") // Suppressing deprecation notification for Context.MODE_MULTI_PROCESS until we
|
||||
// reach a timeline for removal of the whole feature. (Bug 1171213)
|
||||
protected Allocator(Context context) {
|
||||
mPrefs = context.getSharedPreferences("webapps", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ var CastingApps = {
|
||||
mirrorStopMenuId: -1,
|
||||
_blocked: null,
|
||||
_bound: null,
|
||||
_interval: 120 * 1000, // 120 seconds
|
||||
|
||||
init: function ca_init() {
|
||||
if (!this.isCastingEnabled()) {
|
||||
@ -78,8 +79,8 @@ var CastingApps = {
|
||||
mediaPlayerDevice.init();
|
||||
SimpleServiceDiscovery.registerDevice(mediaPlayerDevice);
|
||||
|
||||
// Search for devices continuously every 120 seconds
|
||||
SimpleServiceDiscovery.search(120 * 1000);
|
||||
// Search for devices continuously
|
||||
SimpleServiceDiscovery.search(this._interval);
|
||||
|
||||
this._castMenuId = NativeWindow.contextmenus.add(
|
||||
Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
|
||||
@ -93,6 +94,8 @@ var CastingApps = {
|
||||
Services.obs.addObserver(this, "Casting:Mirror", false);
|
||||
Services.obs.addObserver(this, "ssdp-service-found", false);
|
||||
Services.obs.addObserver(this, "ssdp-service-lost", false);
|
||||
Services.obs.addObserver(this, "application-background", false);
|
||||
Services.obs.addObserver(this, "application-foreground", false);
|
||||
|
||||
BrowserApp.deck.addEventListener("TabSelect", this, true);
|
||||
BrowserApp.deck.addEventListener("pageshow", this, true);
|
||||
@ -195,15 +198,20 @@ var CastingApps = {
|
||||
}
|
||||
break;
|
||||
case "ssdp-service-found":
|
||||
{
|
||||
this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData));
|
||||
break;
|
||||
}
|
||||
this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData));
|
||||
break;
|
||||
case "ssdp-service-lost":
|
||||
{
|
||||
this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData));
|
||||
break;
|
||||
}
|
||||
this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData));
|
||||
break;
|
||||
case "application-background":
|
||||
// Turn off polling while in the background
|
||||
this._interval = SimpleServiceDiscovery.search(0);
|
||||
SimpleServiceDiscovery.stopSearch();
|
||||
break;
|
||||
case "application-foreground":
|
||||
// Turn polling on when app comes back to foreground
|
||||
SimpleServiceDiscovery.search(this._interval);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -17,6 +17,7 @@ this.EXPORTED_SYMBOLS = [
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
@ -458,5 +459,147 @@ this.BrowserTestUtils = {
|
||||
tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Crashes a remote browser tab and cleans up the generated minidumps.
|
||||
* Resolves with the data from the .extra file (the crash annotations).
|
||||
*
|
||||
* @param (Browser) browser
|
||||
* A remote <xul:browser> element. Must not be null.
|
||||
*
|
||||
* @returns (Promise)
|
||||
* @resolves An Object with key-value pairs representing the data from the
|
||||
* crash report's extra file (if applicable).
|
||||
*/
|
||||
crashBrowser: Task.async(function*(browser) {
|
||||
let extra = {};
|
||||
let KeyValueParser = {};
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
Cu.import("resource://gre/modules/KeyValueParser.jsm", KeyValueParser);
|
||||
}
|
||||
|
||||
if (!browser.isRemoteBrowser) {
|
||||
throw new Error("<xul:browser> needs to be remote in order to crash");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory where crash dumps are stored.
|
||||
*
|
||||
* @return nsIFile
|
||||
*/
|
||||
function getMinidumpDirectory() {
|
||||
let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
|
||||
dir.append("minidumps");
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from a directory. This is a no-op if the file does not
|
||||
* exist.
|
||||
*
|
||||
* @param directory
|
||||
* The nsIFile representing the directory to remove from.
|
||||
* @param filename
|
||||
* A string for the file to remove from the directory.
|
||||
*/
|
||||
function removeFile(directory, filename) {
|
||||
let file = directory.clone();
|
||||
file.append(filename);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
// This frame script is injected into the remote browser, and used to
|
||||
// intentionally crash the tab. We crash by using js-ctypes and dereferencing
|
||||
// a bad pointer. The crash should happen immediately upon loading this
|
||||
// frame script.
|
||||
let frame_script = () => {
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
let dies = function() {
|
||||
privateNoteIntentionalCrash();
|
||||
let zero = new ctypes.intptr_t(8);
|
||||
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
|
||||
badptr.contents
|
||||
};
|
||||
|
||||
dump("\nEt tu, Brute?\n");
|
||||
dies();
|
||||
}
|
||||
|
||||
let crashCleanupPromise = new Promise((resolve, reject) => {
|
||||
let observer = (subject, topic, data) => {
|
||||
if (topic != "ipc:content-shutdown") {
|
||||
return reject("Received incorrect observer topic: " + topic);
|
||||
}
|
||||
if (!(subject instanceof Ci.nsIPropertyBag2)) {
|
||||
return reject("Subject did not implement nsIPropertyBag2");
|
||||
}
|
||||
// we might see this called as the process terminates due to previous tests.
|
||||
// We are only looking for "abnormal" exits...
|
||||
if (!subject.hasKey("abnormal")) {
|
||||
dump("\nThis is a normal termination and isn't the one we are looking for...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
let dumpID;
|
||||
if ('nsICrashReporter' in Ci) {
|
||||
dumpID = subject.getPropertyAsAString('dumpID');
|
||||
if (!dumpID) {
|
||||
return reject("dumpID was not present despite crash reporting " +
|
||||
"being enabled");
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpID) {
|
||||
let minidumpDirectory = getMinidumpDirectory();
|
||||
let extrafile = minidumpDirectory.clone();
|
||||
extrafile.append(dumpID + '.extra');
|
||||
if (extrafile.exists()) {
|
||||
dump(`\nNo .extra file for dumpID: ${dumpID}\n`);
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
extra = KeyValueParser.parseKeyValuePairsFromFile(extrafile);
|
||||
} else {
|
||||
dump('\nCrashReporter not enabled - will not return any extra data\n');
|
||||
}
|
||||
}
|
||||
|
||||
removeFile(minidumpDirectory, dumpID + '.dmp');
|
||||
removeFile(minidumpDirectory, dumpID + '.extra');
|
||||
}
|
||||
|
||||
Services.obs.removeObserver(observer, 'ipc:content-shutdown');
|
||||
dump("\nCrash cleaned up\n");
|
||||
resolve();
|
||||
};
|
||||
|
||||
Services.obs.addObserver(observer, 'ipc:content-shutdown', false);
|
||||
});
|
||||
|
||||
let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
|
||||
browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
|
||||
browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
|
||||
dump("\nabout:tabcrashed loaded\n");
|
||||
resolve();
|
||||
}, false, true);
|
||||
});
|
||||
|
||||
// This frame script will crash the remote browser as soon as it is
|
||||
// evaluated.
|
||||
let mm = browser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
|
||||
|
||||
yield Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]);
|
||||
|
||||
let gBrowser = browser.ownerDocument.defaultView.gBrowser;
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (tab.getAttribute("crashed") != "true") {
|
||||
throw new Error("Tab should be marked as crashed");
|
||||
}
|
||||
|
||||
return extra;
|
||||
}),
|
||||
};
|
||||
|
@ -9,72 +9,6 @@
|
||||
// Running this test in ASAN is slow.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves once a remote <xul:browser> has experienced
|
||||
* a crash. Resolves with the data from the .extra file (the crash annotations).
|
||||
*
|
||||
* @param browser
|
||||
* The <xul:browser> that will crash
|
||||
* @return Promise
|
||||
*/
|
||||
function crashBrowser(browser) {
|
||||
let kv = {};
|
||||
Cu.import("resource://gre/modules/KeyValueParser.jsm", kv);
|
||||
// This frame script is injected into the remote browser, and used to
|
||||
// intentionally crash the tab. We crash by using js-ctypes and dereferencing
|
||||
// a bad pointer. The crash should happen immediately upon loading this
|
||||
// frame script.
|
||||
let frame_script = () => {
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
let dies = function() {
|
||||
privateNoteIntentionalCrash();
|
||||
let zero = new ctypes.intptr_t(8);
|
||||
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
|
||||
let crash = badptr.contents;
|
||||
};
|
||||
|
||||
dump("Et tu, Brute?");
|
||||
dies();
|
||||
};
|
||||
|
||||
function checkSubject(subject, data) {
|
||||
return subject instanceof Ci.nsIPropertyBag2 &&
|
||||
subject.hasKey("abnormal");
|
||||
};
|
||||
let crashPromise = TestUtils.topicObserved('ipc:content-shutdown',
|
||||
checkSubject);
|
||||
let crashDataPromise = crashPromise.then(([subject, data]) => {
|
||||
ok(subject instanceof Ci.nsIPropertyBag2);
|
||||
|
||||
let dumpID;
|
||||
if ('nsICrashReporter' in Ci) {
|
||||
dumpID = subject.getPropertyAsAString('dumpID');
|
||||
ok(dumpID, "dumpID is present and not an empty string");
|
||||
}
|
||||
|
||||
let extra = null;
|
||||
if (dumpID) {
|
||||
let minidumpDirectory = getMinidumpDirectory();
|
||||
let extrafile = minidumpDirectory.clone();
|
||||
extrafile.append(dumpID + '.extra');
|
||||
ok(extrafile.exists(), 'found .extra file');
|
||||
extra = kv.parseKeyValuePairsFromFile(extrafile);
|
||||
removeFile(minidumpDirectory, dumpID + '.dmp');
|
||||
removeFile(minidumpDirectory, dumpID + '.extra');
|
||||
}
|
||||
|
||||
return extra;
|
||||
});
|
||||
|
||||
// This frame script will crash the remote browser as soon as it is
|
||||
// evaluated.
|
||||
let mm = browser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
|
||||
return crashDataPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from a directory. This is a no-op if the file does not
|
||||
* exist.
|
||||
@ -130,7 +64,7 @@ add_task(function* test_content_url_annotation() {
|
||||
yield promise;
|
||||
|
||||
// Crash the tab
|
||||
let annotations = yield crashBrowser(browser);
|
||||
let annotations = yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
ok("URL" in annotations, "annotated a URL");
|
||||
is(annotations.URL, redirect_url,
|
||||
|
@ -483,7 +483,7 @@ this.DownloadUtils = {
|
||||
if (aBytes === Infinity) {
|
||||
aBytes = "Infinity";
|
||||
} else {
|
||||
if (Intl) {
|
||||
if (typeof Intl != "undefined") {
|
||||
aBytes = getLocaleNumberFormat(fractionDigits)
|
||||
.format(aBytes);
|
||||
} else if (gDecimalSymbol != ".") {
|
||||
|
@ -406,13 +406,11 @@ xul|button[type="menu"] > xul|menupopup xul|menuseparator {
|
||||
|
||||
/* textboxes */
|
||||
|
||||
*|textbox {
|
||||
html|input[type="text"],
|
||||
html|textarea,
|
||||
xul|textbox {
|
||||
-moz-appearance: none;
|
||||
height: 30px;
|
||||
color: var(--in-content-text-color);
|
||||
line-height: 20px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
border: 1px solid var(--in-content-box-border-color);
|
||||
-moz-border-top-colors: none !important;
|
||||
-moz-border-right-colors: none !important;
|
||||
@ -422,12 +420,27 @@ xul|button[type="menu"] > xul|menupopup xul|menuseparator {
|
||||
background-color: var(--in-content-box-background);
|
||||
}
|
||||
|
||||
html|textbox:focus,
|
||||
xul|textbox {
|
||||
min-height: 30px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
html|input[type="text"],
|
||||
html|textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
html|input[type="text"]:focus,
|
||||
html|textarea:focus,
|
||||
xul|textbox[focused] {
|
||||
border-color: var(--in-content-border-focus);
|
||||
}
|
||||
|
||||
html|textbox:disabled,
|
||||
html|input[type="text"]:disabled,
|
||||
html|textarea:disabled,
|
||||
xul|textbox[disabled="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user