Merge fx-team to m-c with bustage fix, a=merge

This commit is contained in:
Nigel Babu 2014-09-16 08:43:45 +05:30
Родитель 3eee564f75 96f760813a
Коммит 1da2de4cb7
54 изменённых файлов: 912 добавлений и 1557 удалений

Просмотреть файл

@ -258,6 +258,12 @@ XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
*/
function pageShowEventHandlers(persisted) {
XULBrowserWindow.asyncUpdateUI();
// The PluginClickToPlay events are not fired when navigating using the
// BF cache. |persisted| is true when the page is loaded from the
// BF cache, so this code reshows the notification if necessary.
if (persisted)
gPluginHandler.reshowClickToPlayNotification();
}
function UpdateBackForwardCommands(aWebNavigation) {
@ -774,6 +780,13 @@ var gBrowserInit = {
gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
// Note that the XBL binding is untrusted
gBrowser.addEventListener("PluginBindingAttached", gPluginHandler, true, true);
gBrowser.addEventListener("PluginCrashed", gPluginHandler, true);
gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
gBrowser.addEventListener("PluginInstantiated", gPluginHandler, true);
gBrowser.addEventListener("PluginRemoved", gPluginHandler, true);
gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);

Просмотреть файл

@ -16,8 +16,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
"resource://gre/modules/LoginManagerContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
"resource://gre/modules/InsecurePasswordUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluginContent",
"resource:///modules/PluginContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
@ -495,9 +493,6 @@ ClickEventHandler.init();
ContentLinkHandler.init(this);
// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
let pluginContent = new PluginContent(global);
addEventListener("DOMWebNotificationClicked", function(event) {
sendAsyncMessage("DOMWebNotificationClicked", {});
}, false);

Просмотреть файл

@ -1315,11 +1315,11 @@ nsContextMenu.prototype = {
},
playPlugin: function() {
gPluginHandler.contextMenuCommand(this.browser, this.target, "play");
gPluginHandler._showClickToPlayNotification(this.browser, this.target, true);
},
hidePlugin: function() {
gPluginHandler.contextMenuCommand(this.browser, this.target, "hide");
gPluginHandler.hideClickToPlayOverlay(this.target);
},
// Generate email address and put it on clipboard.

Просмотреть файл

@ -132,7 +132,7 @@ function test_multiple_windows() {
let [policy, promise] = sendNotifyRequest("multiple_window_behavior");
let displayCount = 0;
let prefWindowClosed = false;
let prefWindowOpened = false;
let mutationObserversRemoved = false;
function onAlertDisplayed() {
@ -147,8 +147,8 @@ function test_multiple_windows() {
// We register two independent observers and we need both to clean up
// properly. This handles gating for test completion.
function maybeFinish() {
if (!prefWindowClosed) {
dump("Not finishing test yet because pref pane isn't closed.\n");
if (!prefWindowOpened) {
dump("Not finishing test yet because pref pane hasn't yet appeared.\n");
return;
}
@ -193,16 +193,14 @@ function test_multiple_windows() {
is(buttons.length, 1, "There is 1 button in the data reporting notification.");
let button = buttons[0];
// Automatically close preferences window when it is opened as part of
// button press.
// Add an observer to ensure the "advanced" pane opened (but don't bother
// closing it - we close the entire window when done.)
Services.obs.addObserver(function observer(prefWin, topic, data) {
Services.obs.removeObserver(observer, "advanced-pane-loaded");
ok(true, "Advanced preferences opened on info bar button press.");
executeSoon(function soon() {
dump("Closing preferences.\n");
prefWin.close();
prefWindowClosed = true;
prefWindowOpened = true;
maybeFinish();
});
}, "advanced-pane-loaded", false);

Просмотреть файл

@ -8,72 +8,6 @@ const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browse
const gTestRoot = getRootDirectory(gTestPath);
var gTestBrowser = null;
/**
* Frame script that will be injected into the test browser
* to cause the crash, and then manipulate the crashed plugin
* UI. Specifically, after the crash, we ensure that the
* crashed plugin UI is using the right style rules and that
* the submit URL opt-in defaults to checked. Then, we fill in
* a comment with the crash report, uncheck the submit URL
* opt-in, and send the crash reports.
*/
function frameScript() {
function fail(reason) {
sendAsyncMessage("test:crash-plugin:fail", {
reason: `Failure from frameScript: ${reason}`,
});
}
addMessageListener("test:crash-plugin", () => {
let doc = content.document;
addEventListener("PluginCrashed", (event) => {
let plugin = doc.getElementById("test");
if (!plugin) {
fail("Could not find plugin element");
return;
}
let getUI = (anonid) => {
return doc.getAnonymousElementByAttribute(plugin, "anonid", anonid);
};
let style = content.getComputedStyle(getUI("pleaseSubmit"));
if (style.display != "block") {
fail("Submission UI visibility is not correct. Expected block, "
+ " got " + style.display);
return;
}
getUI("submitComment").value = "a test comment";
if (!getUI("submitURLOptIn").checked) {
fail("URL opt-in should default to true.");
return;
}
getUI("submitURLOptIn").click();
getUI("submitButton").click();
});
let plugin = doc.getElementById("test");
try {
plugin.crash()
} catch(e) {
}
});
addMessageListener("test:plugin-submit-status", () => {
let doc = content.document;
let plugin = doc.getElementById("test");
let submitStatusEl =
doc.getAnonymousElementByAttribute(plugin, "anonid", "submitStatus");
let submitStatus = submitStatusEl.getAttribute("status");
sendAsyncMessage("test:plugin-submit-status:result", {
submitStatus: submitStatus,
});
});
}
// Test that plugin crash submissions still work properly after
// click-to-play activation.
@ -97,18 +31,14 @@ function test() {
let tab = gBrowser.loadOneTab("about:blank", { inBackground: false });
gTestBrowser = gBrowser.getBrowserForTab(tab);
let mm = gTestBrowser.messageManager;
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
mm.addMessageListener("test:crash-plugin:fail", (message) => {
ok(false, message.data.reason);
});
gTestBrowser.addEventListener("PluginCrashed", onCrash, false);
gTestBrowser.addEventListener("load", onPageLoad, true);
Services.obs.addObserver(onSubmitStatus, "crash-report-status", false);
registerCleanupFunction(function cleanUp() {
env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
env.set("MOZ_CRASHREPORTER_URL", serverURL);
gTestBrowser.removeEventListener("PluginCrashed", onCrash, false);
gTestBrowser.removeEventListener("load", onPageLoad, true);
Services.obs.removeObserver(onSubmitStatus, "crash-report-status");
gBrowser.removeCurrentTab();
@ -140,8 +70,31 @@ function afterBindingAttached() {
}
function pluginActivated() {
let mm = gTestBrowser.messageManager;
mm.sendAsyncMessage("test:crash-plugin");
let plugin = gTestBrowser.contentDocument.getElementById("test");
try {
plugin.crash();
} catch (e) {
// The plugin crashed in the above call, an exception is expected.
}
}
function onCrash() {
try {
let plugin = gBrowser.contentDocument.getElementById("test");
let elt = gPluginHandler.getPluginUI.bind(gPluginHandler, plugin);
let style =
gBrowser.contentWindow.getComputedStyle(elt("pleaseSubmit"));
is(style.display, "block", "Submission UI visibility should be correct");
elt("submitComment").value = "a test comment";
is(elt("submitURLOptIn").checked, true, "URL opt-in should default to true");
EventUtils.synthesizeMouseAtCenter(elt("submitURLOptIn"), {}, gTestBrowser.contentWindow);
EventUtils.synthesizeMouseAtCenter(elt("submitButton"), {}, gTestBrowser.contentWindow);
// And now wait for the submission status notification.
}
catch (err) {
failWithException(err);
}
}
function onSubmitStatus(subj, topic, data) {
@ -175,23 +128,19 @@ function onSubmitStatus(subj, topic, data) {
ok(val === undefined,
"URL should be absent from extra data when opt-in not checked");
let submitStatus = null;
let mm = gTestBrowser.messageManager;
mm.addMessageListener("test:plugin-submit-status:result", (message) => {
submitStatus = message.data.submitStatus;
// Execute this later in case the event to change submitStatus has not
// have been dispatched yet.
executeSoon(function () {
let plugin = gBrowser.contentDocument.getElementById("test");
let elt = gPluginHandler.getPluginUI.bind(gPluginHandler, plugin);
is(elt("submitStatus").getAttribute("status"), data,
"submitStatus data should match");
});
mm.sendAsyncMessage("test:plugin-submit-status");
let condition = () => submitStatus;
waitForCondition(condition, () => {
is(submitStatus, data, "submitStatus data should match");
finish();
}, "Waiting for submitStatus to be reported from frame script");
}
catch (err) {
failWithException(err);
}
finish();
}
function getPropertyBagValue(bag, key) {

Просмотреть файл

@ -29,11 +29,11 @@ function handleEvent() {
function part1() {
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent);
waitForNotificationPopup("click-to-play-plugins", gBrowser.selectedBrowser, () => {
gNextTest = part2;
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
gNewWindow.addEventListener("load", handleEvent, true);
});
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab");
gNextTest = part2;
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
gNewWindow.addEventListener("load", handleEvent, true);
}
function part2() {
@ -62,10 +62,10 @@ function part4() {
function part5() {
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent);
waitForNotificationPopup("click-to-play-plugins", gBrowser.selectedBrowser, () => {
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
waitForFocus(part6, gNewWindow);
});
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab");
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
waitForFocus(part6, gNewWindow);
}
function part6() {
@ -92,10 +92,8 @@ function part8() {
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, shutdown, "plugin should be activated now");
}
ok(objLoadingContent.activated, "plugin should be activated now");
function shutdown() {
gNewWindow.close();
gNewWindow = null;
finish();

Просмотреть файл

@ -74,15 +74,13 @@ function test1() {
let plugin = doc.getElementById("test");
ok(plugin, "Test 1, Found plugin in page");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay.classList.contains("visible"), "Test 1, Plugin overlay should exist, not be hidden");
let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay.classList.contains("visible"), "Test 1, Plugin overlay should exist, not be hidden");
let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon")
EventUtils.synthesizeMouseAtCenter(closeIcon, {}, frame.contentWindow);
let condition = () => !overlay.classList.contains("visible");
waitForCondition(condition, test2, "Test 1, Waited too long for the overlay to become invisible.");
});
EventUtils.synthesizeMouseAtCenter(closeIcon, {}, frame.contentWindow);
let condition = () => !overlay.classList.contains("visible");
waitForCondition(condition, test2, "Test 1, Waited too long for the overlay to become invisible.");
}
function test2() {

Просмотреть файл

@ -63,37 +63,43 @@ function runAfterPluginBindingAttached(func) {
// Tests for the notification bar for hidden plugins.
function test1() {
info("Test 1 - expecting a notification bar for hidden plugins.");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
waitForNotificationBar("plugin-hidden", gTestBrowser, () => {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 1: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
() => {
// Don't use setTestPluginEnabledState here because we already saved the
// prior value
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
prepareTest(test2, gTestRoot + "plugin_small.html");
});
});
},
"Test 1, expected to have a plugin notification bar");
}
function test2() {
info("Test 2 - expecting no plugin notification bar on visible plugins.");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 2: There should be a plugin notification");
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
() => {
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test3, gTestRoot + "plugin_overlayed.html");
},
"expected to not have a plugin notification bar"
);
});
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
() => {
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test3, gTestRoot + "plugin_overlayed.html");
},
"Test 2, expected to not have a plugin notification bar");
}
function test3() {
info("Test 3 - expecting a plugin notification bar when plugins are overlaid");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
waitForNotificationBar("plugin-hidden", gTestBrowser, test3b);
});
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 3: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test3b,
"Test 3, expected the plugin infobar to be triggered when plugin was overlayed");
}
function test3b()
@ -112,10 +118,13 @@ function test3b()
}
function test4() {
info("Test 4 - expecting a plugin notification bar when plugins are overlaid offscreen")
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
waitForNotificationBar("plugin-hidden", gTestBrowser, test4b);
});
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 4: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test4b,
"Test 4, expected the plugin infobar to be triggered when plugin was overlayed");
}
function test4b() {
@ -132,6 +141,9 @@ function test4b() {
prepareTest(runAfterPluginBindingAttached(test5), gHttpTestRoot + "plugin_small.html");
}
// Test that the notification bar is getting dismissed when directly activating plugins
// via the doorhanger.
function test5() {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
@ -139,27 +151,23 @@ function test5() {
"Test 5, expected a notification bar for hidden plugins");
}
// Test that the notification bar is getting dismissed when directly activating plugins
// via the doorhanger.
function test6() {
info("Test 6 - expecting the doorhanger to be dismissed when directly activating plugins.");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, (notification) => {
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 6, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 6, Plugin should be click-to-play");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 6, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 6, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 6, Plugin should be click-to-play");
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
test7,
"Test 6, expected the notification bar for hidden plugins to get dismissed");
});
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
test7,
"Test 6, expected the notification bar for hidden plugins to get dismissed");
}
function test7() {

Просмотреть файл

@ -60,8 +60,10 @@ function test1b() {
// Click the activate button on doorhanger to make sure it works
popupNotification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
var condition = function() objLoadingContent.activated;
waitForCondition(condition, test1c, "Test 1b, Waited too long for plugin activation");
ok(objLoadingContent.activated, "Test 1b, Doorhanger should activate plugin");
test1c();
}
function test1c() {

Просмотреть файл

@ -36,14 +36,12 @@ function pluginBindingAttached() {
ok(testplugin, "should have test plugin");
var secondtestplugin = doc.getElementById("secondtest");
ok(!secondtestplugin, "should not yet have second test plugin");
var notification;
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, (notification => {
ok(notification, "should have popup notification");
// We don't set up the action list until the notification is shown
notification.reshow();
is(notification.options.pluginData.size, 1, "should be 1 type of plugin in the popup notification");
XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addSecondPlugin();
}));
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "should have popup notification");
// We don't set up the action list until the notification is shown
notification.reshow();
is(notification.options.pluginData.size, 1, "should be 1 type of plugin in the popup notification");
XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addSecondPlugin();
} else if (gNumPluginBindingsAttached == 2) {
var doc = gTestBrowser.contentDocument;
var testplugin = doc.getElementById("test");
@ -53,8 +51,8 @@ function pluginBindingAttached() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "should have popup notification");
notification.reshow();
let condition = () => (notification.options.pluginData.size == 2);
waitForCondition(condition, finish, "Waited too long for 2 types of plugins in popup notification");
is(notification.options.pluginData.size, 2, "should be 2 types of plugin in the popup notification");
finish();
} else {
ok(false, "if we've gotten here, something is quite wrong");
}

Просмотреть файл

@ -59,11 +59,11 @@ function onCrash(event) {
is (propVal, val, "Correct property in detail propBag: " + name + ".");
}
waitForNotificationBar("plugin-crashed", gTestBrowser, (notification) => {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
ok(notification, "Infobar was shown.");
is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM, "Correct priority.");
is(notification.getAttribute("label"), "The GlobalTestPlugin plugin has crashed.", "Correct message.");
finish();
});
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
let notification = notificationBox.getNotificationWithValue("plugin-crashed");
ok(notification, "Infobar was shown.");
is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM, "Correct priority.");
is(notification.getAttribute("label"), "The GlobalTestPlugin plugin has crashed.", "Correct message.");
finish();
}

Просмотреть файл

@ -6,80 +6,9 @@ Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
Cu.import("resource://gre/modules/Services.jsm");
const CRASH_URL = "http://example.com/browser/browser/base/content/test/plugins/plugin_crashCommentAndURL.html";
const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
/**
* Frame script that will be injected into the test browser
* to cause plugin crashes, and then manipulate the crashed plugin
* UI. The specific actions and checks that occur in the frame
* script for the crashed plugin UI are set in the
* test:crash-plugin message object sent from the parent. The actions
* and checks that the parent can specify are:
*
* pleaseSubmitStyle: the display style that the pleaseSubmit anonymous element
* should have - example "block", "none".
* submitComment: the comment that should be put into the crash report
* urlOptIn: true if the submitURLOptIn element should be checked.
* sendCrashMessage: if true, the frame script will send a
* test:crash-plugin:crashed message when the plugin has
* crashed. This is used for the last test case, and
* causes the frame script to skip any of the crashed
* plugin UI manipulation, since the last test shows
* no crashed plugin UI.
*/
function frameScript() {
function fail(reason) {
sendAsyncMessage("test:crash-plugin:fail", {
reason: `Failure from frameScript: ${reason}`,
});
}
addMessageListener("test:crash-plugin", (message) => {
addEventListener("PluginCrashed", function onPluginCrashed(event) {
removeEventListener("PluginCrashed", onPluginCrashed);
let doc = content.document;
let plugin = doc.getElementById("plugin");
if (!plugin) {
fail("Could not find plugin element");
return;
}
let getUI = (anonid) => {
return doc.getAnonymousElementByAttribute(plugin, "anonid", anonid);
};
let style = content.getComputedStyle(getUI("pleaseSubmit"));
if (style.display != message.data.pleaseSubmitStyle) {
fail("Submission UI visibility is not correct. Expected " +
`${message.data.pleaseSubmitStyle} and got ${style.display}`);
return;
}
if (message.data.sendCrashMessage) {
let propBag = event.detail.QueryInterface(Ci.nsIPropertyBag2);
let crashID = propBag.getPropertyAsAString("pluginDumpID");
sendAsyncMessage("test:crash-plugin:crashed", {
crashID: crashID,
});
return;
}
if (message.data.submitComment) {
getUI("submitComment").value = message.data.submitComment;
}
getUI("submitURLOptIn").checked = message.data.urlOptIn;
getUI("submitButton").click();
});
let plugin = content.document.getElementById("test");
try {
plugin.crash()
} catch(e) {
}
});
}
function test() {
// Crashing the plugin takes up a lot of time, so extend the test timeout.
requestLongerTimeout(runs.length);
@ -100,18 +29,14 @@ function test() {
let tab = gBrowser.loadOneTab("about:blank", { inBackground: false });
let browser = gBrowser.getBrowserForTab(tab);
let mm = browser.messageManager;
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
mm.addMessageListener("test:crash-plugin:fail", (message) => {
ok(false, message.data.reason);
});
browser.addEventListener("PluginCrashed", onCrash, false);
Services.obs.addObserver(onSubmitStatus, "crash-report-status", false);
registerCleanupFunction(function cleanUp() {
env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
env.set("MOZ_CRASHREPORTER_URL", serverURL);
gBrowser.selectedBrowser.removeEventListener("PluginCrashed", onCrash,
false);
Services.obs.removeObserver(onSubmitStatus, "crash-report-status");
gBrowser.removeCurrentTab();
});
@ -151,24 +76,6 @@ function doNextRun() {
memo[arg] = currentRun[arg];
return memo;
}, {});
let mm = gBrowser.selectedBrowser.messageManager;
if (!currentRun.shouldSubmittionUIBeVisible) {
mm.addMessageListener("test:crash-plugin:crash", function onCrash(message) {
mm.removeMessageListener("test:crash-plugin:crash", onCrash);
ok(!!message.data.crashID, "pluginDumpID should be set");
CrashSubmit.delete(message.data.crashID);
doNextRun();
});
}
mm.sendAsyncMessage("test:crash-plugin", {
pleaseSubmitStyle: currentRun.shouldSubmissionUIBeVisible ? "block" : "none",
submitComment: currentRun.comment,
urlOptIn: currentRun.urlOptIn,
sendOnCrashMessage: !currentRun.shouldSubmissionUIBeVisible,
});
gBrowser.loadURI(CRASH_URL + "?" +
encodeURIComponent(JSON.stringify(args)));
// And now wait for the crash.
@ -179,6 +86,37 @@ function doNextRun() {
}
}
function onCrash(event) {
try {
let plugin = gBrowser.contentDocument.getElementById("plugin");
let elt = gPluginHandler.getPluginUI.bind(gPluginHandler, plugin);
let style =
gBrowser.contentWindow.getComputedStyle(elt("pleaseSubmit"));
is(style.display,
currentRun.shouldSubmissionUIBeVisible ? "block" : "none",
"Submission UI visibility should be correct");
if (!currentRun.shouldSubmissionUIBeVisible) {
// Done with this run. We don't submit the crash, so we will have to
// remove the dump manually.
let propBag = event.detail.QueryInterface(Ci.nsIPropertyBag2);
let crashID = propBag.getPropertyAsAString("pluginDumpID");
ok(!!crashID, "pluginDumpID should be set");
CrashSubmit.delete(crashID);
doNextRun();
return;
}
elt("submitComment").value = currentRun.comment;
elt("submitURLOptIn").checked = currentRun.urlOptIn;
elt("submitButton").click();
// And now wait for the submission status notification.
}
catch (err) {
failWithException(err);
doNextRun();
}
}
function onSubmitStatus(subj, topic, data) {
try {
// Wait for success or failed, doesn't matter which.

Просмотреть файл

@ -56,7 +56,6 @@ TabOpenListener.prototype = {
function test() {
waitForExplicitFinish();
SimpleTest.requestCompleteLog();
requestLongerTimeout(2);
registerCleanupFunction(function() {
clearAllPluginPermissions();
@ -798,10 +797,7 @@ function test24a() {
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
waitForCondition(() => objLoadingContent.activated, () => {
prepareTest(test24b, gHttpTestRoot + "plugin_test.html");
}, "Test 24a, plugin should now be activated.");
prepareTest(test24b, gHttpTestRoot + "plugin_test.html");
}
// did the "always allow" work as intended?
@ -809,11 +805,11 @@ function test24b() {
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 24b, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, () => {
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginVulnerableUpdatable.xml", () => {
prepareTest(runAfterPluginBindingAttached(test24c), gHttpTestRoot + "plugin_test.html");
});
}, "Test 24b, plugin should be activated");
ok(objLoadingContent.activated, "Test 24b, plugin should be activated");
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginVulnerableUpdatable.xml",
function() {
prepareTest(runAfterPluginBindingAttached(test24c), gHttpTestRoot + "plugin_test.html");
});
}
// the plugin is now blocklisted, so it should not automatically load
@ -824,13 +820,13 @@ function test24c() {
ok(plugin, "Test 24c, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "Test 24c, Plugin should be vulnerable/updatable");
waitForCondition(() => !objLoadingContent.activated, () => {
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
ok(!objLoadingContent.activated, "Test 24c, plugin should not be activated");
prepareTest(test24d, gHttpTestRoot + "plugin_test.html");
}, "Test 24c, plugin should not be activated");
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
prepareTest(test24d, gHttpTestRoot + "plugin_test.html");
}
// We should still be able to always allow a plugin after we've seen that it's
@ -839,14 +835,15 @@ function test24d() {
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 24d, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, () => {
// this resets the vulnerable plugin permission
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", () => {
clearAllPluginPermissions();
resetBlocklist();
prepareTest(test25, gTestRoot + "plugin_syncRemoved.html");
});
}, "Test 24d, plugin should be activated");
ok(objLoadingContent.activated, "Test 24d, plugin should be activated");
// this resets the vulnerable plugin permission
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml",
function() {
clearAllPluginPermissions();
resetBlocklist();
prepareTest(test25, gTestRoot + "plugin_syncRemoved.html");
});
}
function test25() {

Просмотреть файл

@ -108,28 +108,3 @@ function setAndUpdateBlocklist(aURL, aCallback) {
function resetBlocklist() {
Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL);
}
function waitForNotificationPopup(notificationID, browser, callback) {
let notification;
waitForCondition(
() => (notification = PopupNotifications.getNotification(notificationID, browser)),
() => {
ok(notification, `Successfully got the ${notificationID} notification popup`);
callback(notification);
},
`Waited too long for the ${notificationID} notification popup`
);
}
function waitForNotificationBar(notificationID, browser, callback) {
let notification;
let notificationBox = gBrowser.getNotificationBox(browser);
waitForCondition(
() => (notification = notificationBox.getNotificationWithValue(notificationID)),
() => {
ok(notification, `Successfully got the ${notificationID} notification bar`);
callback(notification);
},
`Waited too long for the ${notificationID} notification bar`
);
}

Просмотреть файл

@ -1976,7 +1976,7 @@
return;
}
let host = this.notification.options.host;
let host = gPluginHandler._getHostFromPrincipal(this.notification.browser.contentWindow.document.nodePrincipal);
this._setupDescription("pluginActivateMultiple.message", null, host);
var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox");

Просмотреть файл

@ -11,21 +11,14 @@
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/conversation.css">
</head>
<body class="fx-embedded" onload="loop.conversation.init();">
<body class="fx-embedded">
<div id="messages"></div>
<div id="main"></div>
<script type="text/javascript" src="loop/libs/l10n.js"></script>
<script>
window.OTProperties = {
cdnURL: 'loop/',
};
window.OTProperties.assetURL = window.OTProperties.cdnURL + 'sdk-content/';
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
</script>
<script type="text/javascript" src="loop/js/otconfig.js"></script>
<script type="text/javascript" src="loop/libs/sdk.js"></script>
<script type="text/javascript" src="loop/shared/libs/react-0.11.1.js"></script>
<script type="text/javascript" src="loop/shared/libs/jquery-2.1.0.js"></script>

Просмотреть файл

@ -431,3 +431,5 @@ loop.conversation = (function(OT, mozL10n) {
init: init
};
})(window.OT, document.mozL10n);
document.addEventListener('DOMContentLoaded', loop.conversation.init);

Просмотреть файл

@ -431,3 +431,5 @@ loop.conversation = (function(OT, mozL10n) {
init: init
};
})(window.OT, document.mozL10n);
document.addEventListener('DOMContentLoaded', loop.conversation.init);

Просмотреть файл

@ -0,0 +1,10 @@
/* 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/. */
window.OTProperties = {
cdnURL: 'loop/',
};
window.OTProperties.assetURL = window.OTProperties.cdnURL + 'sdk-content/';
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';

Просмотреть файл

@ -551,3 +551,5 @@ loop.panel = (function(_, mozL10n) {
ToSView: ToSView
};
})(_, document.mozL10n);
document.addEventListener('DOMContentLoaded', loop.panel.init);

Просмотреть файл

@ -551,3 +551,5 @@ loop.panel = (function(_, mozL10n) {
ToSView: ToSView
};
})(_, document.mozL10n);
document.addEventListener('DOMContentLoaded', loop.panel.init);

Просмотреть файл

@ -10,7 +10,7 @@
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/panel.css">
</head>
<body class="panel" onload="loop.panel.init();">
<body class="panel">
<div id="main"></div>

Просмотреть файл

@ -14,6 +14,7 @@ browser.jar:
content/browser/loop/js/client.js (content/js/client.js)
content/browser/loop/js/desktopRouter.js (content/js/desktopRouter.js)
content/browser/loop/js/conversation.js (content/js/conversation.js)
content/browser/loop/js/otconfig.js (content/js/otconfig.js)
content/browser/loop/js/panel.js (content/js/panel.js)
# Shared styles

Просмотреть файл

@ -49,6 +49,9 @@
<script src="conversation_test.js"></script>
<script src="panel_test.js"></script>
<script>
// Stop the default init functions running to avoid conflicts in tests
document.removeEventListener('DOMContentLoaded', loop.panel.init);
document.removeEventListener('DOMContentLoaded', loop.conversation.init);
mocha.run(function () {
$("#mocha").append("<p id='complete'>Complete.</p>");
});

Просмотреть файл

@ -1552,6 +1552,14 @@ var Scratchpad = {
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
},
updateStatusBar: function SP_updateStatusBar(aEventType)
{
var statusBarField = document.getElementById("statusbar-line-col");
let { line, ch } = this.editor.getCursor();
statusBarField.textContent = this.strings.formatStringFromName(
"scratchpad.statusBarLineCol", [ line + 1, ch + 1], 2);
},
/**
* The Scratchpad window load event handler. This method
* initializes the Scratchpad window and source editor.
@ -1613,6 +1621,9 @@ var Scratchpad = {
var lines = initialText.split("\n");
this.editor.on("change", this._onChanged);
// Keep a reference to the bound version for use in onUnload.
this.updateStatusBar = Scratchpad.updateStatusBar.bind(this);
this.editor.on("cursorActivity", this.updateStatusBar);
let okstring = this.strings.GetStringFromName("selfxss.okstring");
let msg = this.strings.formatStringFromName("selfxss.msg", [okstring], 1);
this._onPaste = WebConsoleUtils.pasteHandlerGen(this.editor.container.contentDocument.body,
@ -1698,6 +1709,7 @@ var Scratchpad = {
this._onPaste = null;
}
this.editor.off("change", this._onChanged);
this.editor.off("cursorActivity", this.updateStatusBar);
this.editor.destroy();
this.editor = null;

Просмотреть файл

@ -403,6 +403,7 @@
<tabpanels flex="1"/>
</tabbox>
</hbox>
<toolbar id="statusbar-line-col" class="chromeclass-toolbar"/>
</notificationbox>
</window>

Просмотреть файл

@ -44,6 +44,17 @@ function runTests(sw)
let cursor = sp.editor.getCursor();
is(cursor.line + 1, inputLine, "jumpToLine goto error location (line)");
is(cursor.ch + 1, 1, "jumpToLine goto error location (column)");
}, error => {
ok(false, error);
finish();
}).then(() => {
var statusBarField = sp.editor.container.ownerDocument.querySelector("#statusbar-line-col");
let { line, ch } = sp.editor.getCursor();
is(statusBarField.textContent, sp.strings.formatStringFromName(
"scratchpad.statusBarLineCol", [ line + 1, ch + 1], 2), "statusbar text is correct (" + statusBarField.textContent + ")");
finish();
}, error => {
ok(false, error);
finish();
});
}

Просмотреть файл

@ -366,6 +366,8 @@
@BINPATH@/browser/components/BrowserPlaces.manifest
@BINPATH@/browser/components/devtools-clhandler.manifest
@BINPATH@/browser/components/devtools-clhandler.js
@BINPATH@/browser/components/webideCli.js
@BINPATH@/browser/components/webideComponents.manifest
@BINPATH@/browser/components/Experiments.manifest
@BINPATH@/browser/components/ExperimentsService.js
@BINPATH@/browser/components/translation.manifest

Просмотреть файл

@ -78,6 +78,11 @@ browserContext.notification=This scratchpad executes in the Browser context.
# documentation for Scratchpad on MDN.
help.openDocumentationPage=https://developer.mozilla.org/en/Tools/Scratchpad
# LOCALIZATION NOTE (scratchpad.statusBarLineCol): Line, Column
# information displayed in statusbar when selection is made in
# Scratchpad.
scratchpad.statusBarLineCol = Line %1$S, Col %2$S
# LOCALIZATION NOTE (fileExists.notification): This is the message displayed
# over the top of the the editor when a file does not exist.
fileNoLongerExists.notification=This file no longer exists.

Просмотреть файл

@ -1,992 +0,0 @@
# -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"use strict";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
this.EXPORTED_SYMBOLS = [ "PluginContent" ];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
const url = "chrome://browser/locale/browser.properties";
return Services.strings.createBundle(url);
});
this.PluginContent = function (global) {
this.init(global);
}
PluginContent.prototype = {
init: function (global) {
this.global = global;
// Need to hold onto the content window or else it'll get destroyed
this.content = this.global.content;
// Cache of plugin actions for the current page.
this.pluginData = new Map();
// Note that the XBL binding is untrusted
global.addEventListener("PluginBindingAttached", this, true, true);
global.addEventListener("PluginCrashed", this, true);
global.addEventListener("PluginOutdated", this, true);
global.addEventListener("PluginInstantiated", this, true);
global.addEventListener("PluginRemoved", this, true);
global.addEventListener("unload", this);
global.addEventListener("pageshow", (event) => this.onPageShow(event), true);
global.addMessageListener("BrowserPlugins:ActivatePlugins", this);
global.addMessageListener("BrowserPlugins:NotificationRemoved", this);
global.addMessageListener("BrowserPlugins:NotificationShown", this);
global.addMessageListener("BrowserPlugins:ContextMenuCommand", this);
},
uninit: function() {
delete this.global;
delete this.content;
},
receiveMessage: function (msg) {
switch (msg.name) {
case "BrowserPlugins:ActivatePlugins":
this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
break;
case "BrowserPlugins:NotificationRemoved":
this.clearPluginDataCache();
break;
case "BrowserPlugins:NotificationShown":
setTimeout(() => this.updateNotificationUI(), 0);
break;
case "BrowserPlugins:ContextMenuCommand":
switch (msg.data.command) {
case "play":
this._showClickToPlayNotification(msg.objects.plugin, true);
break;
case "hide":
this.hideClickToPlayOverlay(msg.objects.plugin);
break;
}
break;
}
},
onPageShow: function (event) {
// Ignore events that aren't from the main document.
if (this.global.content && event.target != this.global.content.document) {
return;
}
// The PluginClickToPlay events are not fired when navigating using the
// BF cache. |persisted| is true when the page is loaded from the
// BF cache, so this code reshows the notification if necessary.
if (event.persisted) {
this.reshowClickToPlayNotification();
}
},
getPluginUI: function (plugin, anonid) {
return plugin.ownerDocument.
getAnonymousElementByAttribute(plugin, "anonid", anonid);
},
_getPluginInfo: function (pluginElement) {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);
let tagMimetype;
let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin");
let pluginTag = null;
let permissionString = null;
let fallbackType = null;
let blocklistState = null;
tagMimetype = pluginElement.actualType;
if (tagMimetype == "") {
tagMimetype = pluginElement.type;
}
if (this.isKnownPlugin(pluginElement)) {
pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
pluginName = this.makeNicePluginName(pluginTag.name);
permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
fallbackType = pluginElement.defaultFallbackType;
blocklistState = pluginHost.getBlocklistStateForType(pluginElement.actualType);
// Make state-softblocked == state-notblocked for our purposes,
// they have the same UI. STATE_OUTDATED should not exist for plugin
// items, but let's alias it anyway, just in case.
if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED ||
blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
}
}
return { mimetype: tagMimetype,
pluginName: pluginName,
pluginTag: pluginTag,
permissionString: permissionString,
fallbackType: fallbackType,
blocklistState: blocklistState,
};
},
// Map the plugin's name to a filtered version more suitable for user UI.
makeNicePluginName : function (aName) {
if (aName == "Shockwave Flash")
return "Adobe Flash";
// Clean up the plugin name by stripping off parenthetical clauses,
// trailing version numbers or "plugin".
// EG, "Foo Bar (Linux) Plugin 1.23_02" --> "Foo Bar"
// Do this by first stripping the numbers, etc. off the end, and then
// removing "Plugin" (and then trimming to get rid of any whitespace).
// (Otherwise, something like "Java(TM) Plug-in 1.7.0_07" gets mangled)
let newName = aName.replace(/\(.*?\)/g, "").
replace(/[\s\d\.\-\_\(\)]+$/, "").
replace(/\bplug-?in\b/i, "").trim();
return newName;
},
/**
* Update the visibility of the plugin overlay.
*/
setVisibility : function (plugin, overlay, shouldShow) {
overlay.classList.toggle("visible", shouldShow);
},
/**
* Check whether the plugin should be visible on the page. A plugin should
* not be visible if the overlay is too big, or if any other page content
* overlays it.
*
* This function will handle showing or hiding the overlay.
* @returns true if the plugin is invisible.
*/
shouldShowOverlay : function (plugin, overlay) {
// If the overlay size is 0, we haven't done layout yet. Presume that
// plugins are visible until we know otherwise.
if (overlay.scrollWidth == 0) {
return true;
}
// Is the <object>'s size too small to hold what we want to show?
let pluginRect = plugin.getBoundingClientRect();
// XXX bug 446693. The text-shadow on the submitted-report text at
// the bottom causes scrollHeight to be larger than it should be.
let overflows = (overlay.scrollWidth > Math.ceil(pluginRect.width)) ||
(overlay.scrollHeight - 5 > Math.ceil(pluginRect.height));
if (overflows) {
return false;
}
// Is the plugin covered up by other content so that it is not clickable?
// Floating point can confuse .elementFromPoint, so inset just a bit
let left = pluginRect.left + 2;
let right = pluginRect.right - 2;
let top = pluginRect.top + 2;
let bottom = pluginRect.bottom - 2;
let centerX = left + (right - left) / 2;
let centerY = top + (bottom - top) / 2;
let points = [[left, top],
[left, bottom],
[right, top],
[right, bottom],
[centerX, centerY]];
if (right <= 0 || top <= 0) {
return false;
}
let contentWindow = plugin.ownerDocument.defaultView;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
for (let [x, y] of points) {
let el = cwu.elementFromPoint(x, y, true, true);
if (el !== plugin) {
return false;
}
}
return true;
},
addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
let self = this;
let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
linkNode.addEventListener("click",
function(evt) {
if (!evt.isTrusted)
return;
evt.preventDefault();
if (callbackArgs.length == 0)
callbackArgs = [ evt ];
(self[callbackName]).apply(self, callbackArgs);
},
true);
linkNode.addEventListener("keydown",
function(evt) {
if (!evt.isTrusted)
return;
if (evt.keyCode == evt.DOM_VK_RETURN) {
evt.preventDefault();
if (callbackArgs.length == 0)
callbackArgs = [ evt ];
evt.preventDefault();
(self[callbackName]).apply(self, callbackArgs);
}
},
true);
},
// Helper to get the binding handler type from a plugin object
_getBindingType : function(plugin) {
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return null;
switch (plugin.pluginFallbackType) {
case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
return "PluginNotFound";
case Ci.nsIObjectLoadingContent.PLUGIN_DISABLED:
return "PluginDisabled";
case Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED:
return "PluginBlocklisted";
case Ci.nsIObjectLoadingContent.PLUGIN_OUTDATED:
return "PluginOutdated";
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
return "PluginClickToPlay";
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
return "PluginVulnerableUpdatable";
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
return "PluginVulnerableNoUpdate";
case Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW:
return "PluginPlayPreview";
default:
// Not all states map to a handler
return null;
}
},
handleEvent: function (event) {
let eventType = event.type;
if (eventType == "unload") {
this.uninit();
return;
}
if (eventType == "PluginRemoved") {
this.updateNotificationUI();
return;
}
if (eventType == "click") {
this.onOverlayClick(event);
return;
}
if (eventType == "PluginCrashed" &&
!(event.target instanceof Ci.nsIObjectLoadingContent)) {
// If the event target is not a plugin object (i.e., an <object> or
// <embed> element), this call is for a window-global plugin.
this.pluginInstanceCrashed(event.target, event);
return;
}
let plugin = event.target;
let doc = plugin.ownerDocument;
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return;
if (eventType == "PluginBindingAttached") {
// The plugin binding fires this event when it is created.
// As an untrusted event, ensure that this object actually has a binding
// and make sure we don't handle it twice
let overlay = this.getPluginUI(plugin, "main");
if (!overlay || overlay._bindingHandled) {
return;
}
overlay._bindingHandled = true;
// Lookup the handler for this binding
eventType = this._getBindingType(plugin);
if (!eventType) {
// Not all bindings have handlers
return;
}
}
let shouldShowNotification = false;
switch (eventType) {
case "PluginCrashed":
this.pluginInstanceCrashed(plugin, event);
break;
case "PluginNotFound": {
let installable = this.showInstallNotification(plugin, eventType);
let contentWindow = plugin.ownerDocument.defaultView;
// For non-object plugin tags, register a click handler to install the
// plugin. Object tags can, and often do, deal with that themselves,
// so don't stomp on the page developers toes.
if (installable && !(plugin instanceof contentWindow.HTMLObjectElement)) {
let installStatus = this.getPluginUI(plugin, "installStatus");
installStatus.setAttribute("installable", "true");
let iconStatus = this.getPluginUI(plugin, "icon");
iconStatus.setAttribute("installable", "true");
let installLink = this.getPluginUI(plugin, "installPluginLink");
this.addLinkClickCallback(installLink, "installSinglePlugin", plugin);
}
break;
}
case "PluginBlocklisted":
case "PluginOutdated":
shouldShowNotification = true;
break;
case "PluginVulnerableUpdatable":
let updateLink = this.getPluginUI(plugin, "checkForUpdatesLink");
this.addLinkClickCallback(updateLink, "forwardCallback", "openPluginUpdatePage");
/* FALLTHRU */
case "PluginVulnerableNoUpdate":
case "PluginClickToPlay":
this._handleClickToPlayEvent(plugin);
let overlay = this.getPluginUI(plugin, "main");
let pluginName = this._getPluginInfo(plugin).pluginName;
let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate", [pluginName], 1);
let overlayText = this.getPluginUI(plugin, "clickToPlay");
overlayText.textContent = messageString;
if (eventType == "PluginVulnerableUpdatable" ||
eventType == "PluginVulnerableNoUpdate") {
let vulnerabilityString = gNavigatorBundle.GetStringFromName(eventType);
let vulnerabilityText = this.getPluginUI(plugin, "vulnerabilityStatus");
vulnerabilityText.textContent = vulnerabilityString;
}
shouldShowNotification = true;
break;
case "PluginPlayPreview":
this._handlePlayPreviewEvent(plugin);
break;
case "PluginDisabled":
let manageLink = this.getPluginUI(plugin, "managePluginsLink");
this.addLinkClickCallback(manageLink, "forwardCallback", "managePlugins");
shouldShowNotification = true;
break;
case "PluginInstantiated":
shouldShowNotification = true;
break;
}
// Show the in-content UI if it's not too big. The crashed plugin handler already did this.
if (eventType != "PluginCrashed") {
let overlay = this.getPluginUI(plugin, "main");
if (overlay != null) {
this.setVisibility(plugin, overlay,
this.shouldShowOverlay(plugin, overlay));
let resizeListener = (event) => {
this.setVisibility(plugin, overlay,
this.shouldShowOverlay(plugin, overlay));
this.updateNotificationUI();
};
plugin.addEventListener("overflow", resizeListener);
plugin.addEventListener("underflow", resizeListener);
}
}
let closeIcon = this.getPluginUI(plugin, "closeIcon");
if (closeIcon) {
closeIcon.addEventListener("click", event => {
if (event.button == 0 && event.isTrusted)
this.hideClickToPlayOverlay(plugin);
}, true);
}
if (shouldShowNotification) {
this._showClickToPlayNotification(plugin, false);
}
},
isKnownPlugin: function (objLoadingContent) {
return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
},
canActivatePlugin: function (objLoadingContent) {
// if this isn't a known plugin, we can't activate it
// (this also guards pluginHost.getPermissionStringForType against
// unexpected input)
if (!this.isKnownPlugin(objLoadingContent))
return false;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
let principal = objLoadingContent.ownerDocument.defaultView.top.document.nodePrincipal;
let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
let isFallbackTypeValid =
objLoadingContent.pluginFallbackType >= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
objLoadingContent.pluginFallbackType <= Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE;
if (objLoadingContent.pluginFallbackType == Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW) {
// checking if play preview is subject to CTP rules
let playPreviewInfo = pluginHost.getPlayPreviewInfo(objLoadingContent.actualType);
isFallbackTypeValid = !playPreviewInfo.ignoreCTP;
}
return !objLoadingContent.activated &&
pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
isFallbackTypeValid;
},
hideClickToPlayOverlay: function (plugin) {
let overlay = this.getPluginUI(plugin, "main");
if (overlay) {
overlay.classList.remove("visible");
}
},
stopPlayPreview: function (plugin, playPlugin) {
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (objLoadingContent.activated)
return;
if (playPlugin)
objLoadingContent.playPlugin();
else
objLoadingContent.cancelPlayPreview();
},
// Callback for user clicking on a missing (unsupported) plugin.
installSinglePlugin: function (plugin) {
this.global.sendAsyncMessage("PluginContent:InstallSinglePlugin", {
pluginInfo: this._getPluginInfo(plugin),
});
},
// Forward a link click callback to the chrome process.
forwardCallback: function (name) {
this.global.sendAsyncMessage("PluginContent:LinkClickCallback", { name: name });
},
#ifdef MOZ_CRASHREPORTER
submitReport: function submitReport(pluginDumpID, browserDumpID, plugin) {
let keyVals = {};
if (plugin) {
let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
if (userComment)
keyVals.PluginUserComment = userComment;
if (this.getPluginUI(plugin, "submitURLOptIn").checked)
keyVals.PluginContentURL = plugin.ownerDocument.URL;
}
this.global.sendAsyncMessage("PluginContent:SubmitReport", {
pluginDumpID: pluginDumpID,
browserDumpID: browserDumpID,
keyVals: keyVals,
});
},
#endif
reloadPage: function () {
this.global.content.location.reload();
},
showInstallNotification: function (plugin) {
let [shown] = this.global.sendSyncMessage("PluginContent:ShowInstallNotification", {
pluginInfo: this._getPluginInfo(plugin),
});
return shown;
},
// Event listener for click-to-play plugins.
_handleClickToPlayEvent: function (plugin) {
let doc = plugin.ownerDocument;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
// guard against giving pluginHost.getPermissionStringForType a type
// not associated with any known plugin
if (!this.isKnownPlugin(objLoadingContent))
return;
let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
let principal = doc.defaultView.top.document.nodePrincipal;
let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
let overlay = this.getPluginUI(plugin, "main");
if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION) {
if (overlay) {
overlay.classList.remove("visible");
}
return;
}
if (overlay) {
overlay.addEventListener("click", this, true);
}
},
onOverlayClick: function (event) {
let document = event.target.ownerDocument;
let plugin = document.getBindingParent(event.target);
let contentWindow = plugin.ownerDocument.defaultView.top;
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
// Have to check that the target is not the link to update the plugin
if (!(event.originalTarget instanceof contentWindow.HTMLAnchorElement) &&
(event.originalTarget.getAttribute('anonid') != 'closeIcon') &&
event.button == 0 && event.isTrusted) {
this._showClickToPlayNotification(plugin, true);
event.stopPropagation();
event.preventDefault();
}
},
_handlePlayPreviewEvent: function (plugin) {
let doc = plugin.ownerDocument;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let pluginInfo = this._getPluginInfo(plugin);
let playPreviewInfo = pluginHost.getPlayPreviewInfo(pluginInfo.mimetype);
let previewContent = this.getPluginUI(plugin, "previewPluginContent");
let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0];
if (!iframe) {
// lazy initialization of the iframe
iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
iframe.className = "previewPluginContentFrame";
previewContent.appendChild(iframe);
// Force a style flush, so that we ensure our binding is attached.
plugin.clientTop;
}
iframe.src = playPreviewInfo.redirectURL;
// MozPlayPlugin event can be dispatched from the extension chrome
// code to replace the preview content with the native plugin
let playPluginHandler = (event) => {
if (!event.isTrusted)
return;
previewContent.removeEventListener("MozPlayPlugin", playPluginHandler, true);
let playPlugin = !event.detail;
this.stopPlayPreview(plugin, playPlugin);
// cleaning up: removes overlay iframe from the DOM
let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0];
if (iframe)
previewContent.removeChild(iframe);
};
previewContent.addEventListener("MozPlayPlugin", playPluginHandler, true);
if (!playPreviewInfo.ignoreCTP) {
this._showClickToPlayNotification(plugin, false);
}
},
reshowClickToPlayNotification: function () {
let contentWindow = this.global.content;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let plugins = cwu.plugins;
for (let plugin of plugins) {
let overlay = this.getPluginUI(plugin, "main");
if (overlay)
overlay.removeEventListener("click", this, true);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (this.canActivatePlugin(objLoadingContent))
this._handleClickToPlayEvent(plugin);
}
this._showClickToPlayNotification(null, false);
},
// Match the behaviour of nsPermissionManager
_getHostFromPrincipal: function (principal) {
if (!principal.URI || principal.URI.schemeIs("moz-nullprincipal")) {
return "(null)";
}
try {
if (principal.URI.host)
return principal.URI.host;
} catch (e) {}
return principal.origin;
},
/**
* Activate the plugins that the user has specified.
*/
activatePlugins: function (pluginInfo, newState) {
let contentWindow = this.global.content;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let plugins = cwu.plugins;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let pluginFound = false;
for (let plugin of plugins) {
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (!this.isKnownPlugin(plugin)) {
continue;
}
if (pluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
pluginFound = true;
if (newState == "block") {
plugin.reload(true);
} else {
if (this.canActivatePlugin(plugin)) {
let overlay = this.getPluginUI(plugin, "main");
if (overlay) {
overlay.removeEventListener("click", this, true);
}
plugin.playPlugin();
}
}
}
}
// If there are no instances of the plugin on the page any more, what the
// user probably needs is for us to allow and then refresh.
if (newState != "block" && !pluginFound) {
this.reloadPage();
}
this.updateNotificationUI();
},
_showClickToPlayNotification: function (plugin, showNow) {
let plugins = [];
// If plugin is null, that means the user has navigated back to a page with
// plugins, and we need to collect all the plugins.
if (plugin === null) {
let contentWindow = this.global.content;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
// cwu.plugins may contain non-plugin <object>s, filter them out
plugins = cwu.plugins.filter((plugin) =>
plugin.getContentTypeForMIMEType(plugin.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
if (plugins.length == 0) {
this.removeNotification("click-to-play-plugins");
return;
}
} else {
plugins = [plugin];
}
let pluginData = this.pluginData;
let principal = this.global.content.document.nodePrincipal;
let principalHost = this._getHostFromPrincipal(principal);
for (let p of plugins) {
let pluginInfo = this._getPluginInfo(p);
if (pluginInfo.permissionString === null) {
Cu.reportError("No permission string for active plugin.");
continue;
}
if (pluginData.has(pluginInfo.permissionString)) {
continue;
}
let permissionObj = Services.perms.
getPermissionObject(principal, pluginInfo.permissionString, false);
if (permissionObj) {
pluginInfo.pluginPermissionHost = permissionObj.host;
pluginInfo.pluginPermissionType = permissionObj.expireType;
}
else {
pluginInfo.pluginPermissionHost = principalHost;
pluginInfo.pluginPermissionType = undefined;
}
this.pluginData.set(pluginInfo.permissionString, pluginInfo);
}
this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
plugins: [... this.pluginData.values()],
showNow: showNow,
host: principalHost,
}, null, principal);
},
updateNotificationUI: function () {
// Make a copy of the actions from the last popup notification.
let haveInsecure = false;
let actions = new Map();
for (let action of this.pluginData.values()) {
switch (action.fallbackType) {
// haveInsecure will trigger the red flashing icon and the infobar
// styling below
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
haveInsecure = true;
// fall through
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
actions.set(action.permissionString, action);
continue;
}
}
// Remove plugins that are already active, or large enough to show an overlay.
let contentWindow = this.global.content;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
for (let plugin of cwu.plugins) {
let info = this._getPluginInfo(plugin);
if (!actions.has(info.permissionString)) {
continue;
}
let fallbackType = info.fallbackType;
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
actions.delete(info.permissionString);
if (actions.size == 0) {
break;
}
continue;
}
if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE &&
fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
continue;
}
let overlay = this.getPluginUI(plugin, "main");
if (!overlay) {
continue;
}
let shouldShow = this.shouldShowOverlay(plugin, overlay);
this.setVisibility(plugin, overlay, shouldShow);
if (shouldShow) {
actions.delete(info.permissionString);
if (actions.size == 0) {
break;
}
}
}
// If there are any items remaining in `actions` now, they are hidden
// plugins that need a notification bar.
let principal = contentWindow.document.nodePrincipal;
this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
haveInsecure: haveInsecure,
actions: [... actions.values()],
host: this._getHostFromPrincipal(principal),
}, null, principal);
},
removeNotification: function (name) {
this.global.sendAsyncMessage("PluginContent:RemoveNotification", { name: name });
},
clearPluginDataCache: function () {
this.pluginData.clear();
},
hideNotificationBar: function (name) {
this.global.sendAsyncMessage("PluginContent:HideNotificationBar", { name: name });
},
// Crashed-plugin event listener. Called for every instance of a
// plugin in content.
pluginInstanceCrashed: function (target, aEvent) {
// Ensure the plugin and event are of the right type.
if (!(aEvent instanceof Ci.nsIDOMCustomEvent))
return;
let propBag = aEvent.detail.QueryInterface(Ci.nsIPropertyBag2);
let submittedReport = propBag.getPropertyAsBool("submittedCrashReport");
let doPrompt = true; // XXX followup for .getPropertyAsBool("doPrompt");
let submitReports = true; // XXX followup for .getPropertyAsBool("submitReports");
let pluginName = propBag.getPropertyAsAString("pluginName");
let pluginDumpID = propBag.getPropertyAsAString("pluginDumpID");
let browserDumpID = null;
let gmpPlugin = false;
try {
browserDumpID = propBag.getPropertyAsAString("browserDumpID");
} catch (e) {
// For GMP crashes we don't get a browser dump.
}
try {
gmpPlugin = propBag.getPropertyAsBool("gmpPlugin");
} catch (e) {
// This property is only set for GMP plugins.
}
// For non-GMP plugins, remap the plugin name to a more user-presentable form.
if (!gmpPlugin) {
pluginName = this.makeNicePluginName(pluginName);
}
let messageString = gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title", [pluginName], 1);
let plugin = null, doc;
if (target instanceof Ci.nsIObjectLoadingContent) {
plugin = target;
doc = plugin.ownerDocument;
} else {
doc = target.document;
if (!doc) {
return;
}
// doPrompt is specific to the crashed plugin overlay, and
// therefore is not applicable for window-global plugins.
doPrompt = false;
}
let status;
#ifdef MOZ_CRASHREPORTER
// Determine which message to show regarding crash reports.
if (submittedReport) { // submitReports && !doPrompt, handled in observer
status = "submitted";
}
else if (!submitReports && !doPrompt) {
status = "noSubmit";
}
else if (!pluginDumpID) {
// If we don't have a minidumpID, we can't (or didn't) submit anything.
// This can happen if the plugin is killed from the task manager.
status = "noReport";
}
else {
status = "please";
}
// If we don't have a minidumpID, we can't (or didn't) submit anything.
// This can happen if the plugin is killed from the task manager.
if (!pluginDumpID) {
status = "noReport";
}
// If we're showing the link to manually trigger report submission, we'll
// want to be able to update all the instances of the UI for this crash to
// show an updated message when a report is submitted.
if (doPrompt) {
let observer = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
observe : (subject, topic, data) => {
let propertyBag = subject;
if (!(propertyBag instanceof Ci.nsIPropertyBag2))
return;
// Ignore notifications for other crashes.
if (propertyBag.get("minidumpID") != pluginDumpID)
return;
let statusDiv = this.getPluginUI(plugin, "submitStatus");
statusDiv.setAttribute("status", data);
},
handleEvent : function(event) {
// Not expected to be called, just here for the closure.
}
}
// Use a weak reference, so we don't have to remove it...
Services.obs.addObserver(observer, "crash-report-status", true);
// ...alas, now we need something to hold a strong reference to prevent
// it from being GC. But I don't want to manually manage the reference's
// lifetime (which should be no greater than the page).
// Clever solution? Use a closue with an event listener on the document.
// When the doc goes away, so do the listener references and the closure.
doc.addEventListener("mozCleverClosureHack", observer, false);
}
#endif
let isShowing = false;
if (plugin) {
// If there's no plugin (an <object> or <embed> element), this call is
// for a window-global plugin. In this case, there's no overlay to show.
isShowing = _setUpPluginOverlay.call(this, plugin, doPrompt);
}
if (isShowing) {
// If a previous plugin on the page was too small and resulted in adding a
// notification bar, then remove it because this plugin instance it big
// enough to serve as in-content notification.
this.hideNotificationBar("plugin-crashed");
doc.mozNoPluginCrashedNotification = true;
} else {
// If another plugin on the page was large enough to show our UI, we don't
// want to show a notification bar.
if (!doc.mozNoPluginCrashedNotification) {
this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification", {
messageString: messageString,
pluginDumpID: pluginDumpID,
browserDumpID: browserDumpID,
});
// Remove the notification when the page is reloaded.
doc.defaultView.top.addEventListener("unload", event => {
this.hideNotificationBar("plugin-crashed");
}, false);
}
}
// Configure the crashed-plugin placeholder.
// Returns true if the plugin overlay is visible.
function _setUpPluginOverlay(plugin, doPromptSubmit) {
if (!plugin) {
return false;
}
// Force a layout flush so the binding is attached.
plugin.clientTop;
let overlay = this.getPluginUI(plugin, "main");
let statusDiv = this.getPluginUI(plugin, "submitStatus");
if (doPromptSubmit) {
this.getPluginUI(plugin, "submitButton").addEventListener("click",
function (event) {
if (event.button != 0 || !event.isTrusted)
return;
this.submitReport(pluginDumpID, browserDumpID, plugin);
pref.setBoolPref("", optInCB.checked);
}.bind(this));
let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
optInCB.checked = pref.getBoolPref("");
}
statusDiv.setAttribute("status", status);
let helpIcon = this.getPluginUI(plugin, "helpIcon");
this.addLinkClickCallback(helpIcon, "openHelpPage");
let crashText = this.getPluginUI(plugin, "crashedText");
crashText.textContent = messageString;
let link = this.getPluginUI(plugin, "reloadLink");
this.addLinkClickCallback(link, "reloadPage");
let isShowing = this.shouldShowOverlay(plugin, overlay);
// Is the <object>'s size too small to hold what we want to show?
if (!isShowing) {
// First try hiding the crash report submission UI.
statusDiv.removeAttribute("status");
isShowing = this.shouldShowOverlay(plugin, overlay);
}
this.setVisibility(plugin, overlay, isShowing);
return isShowing;
}
}
};

Просмотреть файл

@ -44,7 +44,6 @@ if CONFIG['NIGHTLY_BUILD']:
EXTRA_PP_JS_MODULES += [
'AboutHome.jsm',
'PluginContent.jsm',
'RecentWindow.jsm',
'UITour.jsm',
'webrtcUI.jsm',

Просмотреть файл

@ -102,4 +102,3 @@ skip-if = e10s
skip-if = e10s
[browser_timelineMarkers-01.js]
[browser_timelineMarkers-02.js]
skip-if = e10s # Bug 1064848

Просмотреть файл

@ -965,6 +965,10 @@ public abstract class GeckoApp
}
if (image != null) {
String path = Media.insertImage(getContentResolver(),image, null, null);
if (path == null) {
Toast.makeText((Context) this, R.string.set_image_path_fail, Toast.LENGTH_SHORT).show();
return;
}
final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse(path));

Просмотреть файл

@ -294,6 +294,7 @@ size. -->
<!ENTITY tabs_private "Private">
<!ENTITY tabs_synced "Synced">
<!ENTITY set_image_fail "Unable to set image">
<!ENTITY set_image_path_fail "Unable to save image">
<!ENTITY set_image_chooser_title "Set Image As">
<!-- Localization note (find_text, find_prev, find_next, find_close) : These strings are used

Просмотреть файл

@ -72,7 +72,9 @@
</style>
<style name="Widget.Home.HistoryListView">
<item name="topDivider">true</item>
<item name="android:paddingLeft">50dp</item>
<item name="android:paddingRight">100dp</item>
<item name="android:paddingTop">0dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
</style>

Просмотреть файл

@ -24,7 +24,11 @@
</style>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">120dp</item>
<item name="android:paddingRight">120dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.GridView">

Просмотреть файл

@ -62,7 +62,11 @@
</style>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.GridView">
@ -74,9 +78,6 @@
</style>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="topDivider">false</item>
</style>

Просмотреть файл

@ -4,12 +4,6 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">120dp</item>
<item name="android:paddingRight">120dp</item>
<item name="topDivider">false</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.GridView">
<item name="android:paddingLeft">55dp</item>
@ -20,6 +14,9 @@
</style>
<style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">50dp</item>
<item name="android:paddingRight">100dp</item>
<item name="android:paddingTop">30dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item>
</style>

Просмотреть файл

@ -17,6 +17,9 @@
</style>
<style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:paddingTop">30dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item>
</style>
@ -27,11 +30,4 @@
<item name="android:paddingRight">212dp</item>
</style>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="topDivider">false</item>
</style>
</resources>

Просмотреть файл

@ -63,10 +63,8 @@
</style>
<style name="Widget.Home.HistoryListView">
<item name="android:paddingTop">0dip</item>
<item name="android:paddingRight">0dip</item>
<item name="android:paddingLeft">0dip</item>
<item name="topDivider">true</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
</style>

Просмотреть файл

@ -436,6 +436,7 @@
<!-- Set Image Notifications -->
<string name="set_image_fail">&set_image_fail;</string>
<string name="set_image_path_fail">&set_image_path_fail;</string>
<string name="set_image_chooser_title">&set_image_chooser_title;</string>
<!-- Contacts API -->

Просмотреть файл

@ -9,6 +9,7 @@
const DIV_POINT_TEXT = "Under";
const INPUT_TEXT = "Text for select all in an <input>";
const TEXTAREA_TEXT = "Text for select all in a <textarea>";
const READONLY_INPUT_TEXT = "readOnly text";
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
@ -24,6 +25,7 @@ function startTests() {
then(testSelectDivAtPoint).
then(testSelectInput).
then(testSelectTextarea).
then(testReadonlyInput).
then(testCloseSelection).
then(finishTests, function(err) {
ok(false, "Error in selection test " + err);
@ -155,6 +157,27 @@ function testSelectTextarea() {
});
}
/* =================================================================================
*
* Ensure that readonly inputs aren't editable, and not subject to
* SelectionHandler actions such as "cut".
*
*/
function testReadonlyInput() {
var sh = getSelectionHandler();
var readOnlyNode = document.getElementById("readOnlyTextInput");
readOnlyNode.value = READONLY_INPUT_TEXT;
return Promise.all([
ok(!sh.isSelectionActive(), "Selection should not be active at start of testReadonlyInput"),
]).then(function() {
return Promise.all([
ok(!sh.isElementEditableText(readOnlyNode), "Selected element should be readOnly (not editable)"),
]);
});
}
/* =================================================================================
*
* Various text selection tests to end active selections, including:
@ -287,5 +310,7 @@ function is(one, two, msg) {
<textarea id="textareaNode"></textarea><br>
<input id="readOnlyTextInput" type="text" readonly><br>
</body>
</html>

Просмотреть файл

@ -9,7 +9,6 @@ import android.content.ContentValues;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
@ -26,6 +25,9 @@ import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
import org.mozilla.search.autocomplete.ClearableEditText;
import org.mozilla.search.autocomplete.SuggestionsFragment;
import org.mozilla.search.providers.SearchEngine;
import org.mozilla.search.providers.SearchEngineManager;
import org.mozilla.search.providers.SearchEngineManager.SearchEngineCallback;
/**
* The main entrance for the Android search intent.
@ -33,7 +35,8 @@ import org.mozilla.search.autocomplete.SuggestionsFragment;
* State management is delegated to child fragments. Fragments communicate
* with each other by passing messages through this activity.
*/
public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implements AcceptsSearchQuery {
public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity
implements AcceptsSearchQuery, SearchEngineCallback {
private static final String KEY_SEARCH_STATE = "search_state";
private static final String KEY_EDIT_STATE = "edit_state";
@ -53,6 +56,14 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
private SearchState searchState = SearchState.PRESEARCH;
private EditState editState = EditState.WAITING;
private SearchEngineManager searchEngineManager;
// Only accessed on the main thread.
private SearchEngine engine;
private SuggestionsFragment suggestionsFragment;
private PostSearchFragment postSearchFragment;
private AsyncQueryHandler queryHandler;
// Main views in layout.
@ -63,7 +74,6 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
private View settingsButton;
private View suggestions;
private SuggestionsFragment suggestionsFragment;
private static final int SUGGESTION_TRANSITION_DURATION = 300;
private static final Interpolator SUGGESTION_TRANSITION_INTERPOLATOR =
@ -85,6 +95,15 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
super.onCreate(savedInstanceState);
setContentView(R.layout.search_activity_main);
suggestionsFragment = (SuggestionsFragment) getSupportFragmentManager().findFragmentById(R.id.suggestions);
postSearchFragment = (PostSearchFragment) getSupportFragmentManager().findFragmentById(R.id.postsearch);
searchEngineManager = new SearchEngineManager(this);
searchEngineManager.setChangeCallback(this);
// Initialize the fragments with the selected search engine.
searchEngineManager.getEngine(this);
queryHandler = new AsyncQueryHandler(getContentResolver()) {};
editText = (ClearableEditText) findViewById(R.id.search_edit_text);
@ -133,7 +152,6 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
});
suggestions = findViewById(R.id.suggestions);
suggestionsFragment = (SuggestionsFragment) getSupportFragmentManager().findFragmentById(R.id.suggestions);
animationText = (TextView) findViewById(R.id.animation_text);
animationCard = findViewById(R.id.animation_card);
@ -151,8 +169,7 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
// If we're in the postsearch state, we need to re-do the query.
if (searchState == SearchState.POSTSEARCH) {
((PostSearchFragment) getSupportFragmentManager().findFragmentById(R.id.postsearch))
.startSearch(query);
startSearch(query);
}
} else {
// If there isn't a state to restore, the activity will start in the presearch state,
@ -164,12 +181,16 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
@Override
protected void onDestroy() {
super.onDestroy();
searchEngineManager.destroy();
searchEngineManager = null;
engine = null;
suggestionsFragment = null;
postSearchFragment = null;
queryHandler = null;
editText = null;
preSearch = null;
postSearch = null;
settingsButton = null;
suggestionsFragment = null;
suggestions = null;
animationText = null;
animationCard = null;
@ -221,8 +242,7 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
public void onSearch(String query, SuggestionAnimation suggestionAnimation) {
storeQuery(query);
((PostSearchFragment) getSupportFragmentManager().findFragmentById(R.id.postsearch))
.startSearch(query);
startSearch(query);
if (suggestionAnimation != null) {
// Animate the suggestion card if start bounds are specified.
@ -234,6 +254,35 @@ public class MainActivity extends LocaleAware.LocaleAwareFragmentActivity implem
}
}
private void startSearch(final String query) {
if (engine != null) {
postSearchFragment.startSearch(engine, query);
return;
}
// engine will only be null if startSearch is called before the getEngine
// call in onCreate is completed.
searchEngineManager.getEngine(new SearchEngineCallback() {
@Override
public void execute(SearchEngine engine) {
postSearchFragment.startSearch(engine, query);
}
});
}
/**
* This method is called when we fetch the current engine in onCreate,
* as well as whenever the current engine changes. This method will only
* ever be called on the main thread.
*
* @param engine The current search engine.
*/
@Override
public void execute(SearchEngine engine) {
this.engine = engine;
suggestionsFragment.setEngine(engine);
}
/**
* Animates search suggestion to search bar. This animation has 2 main parts:
*

Просмотреть файл

@ -4,7 +4,6 @@
package org.mozilla.search;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
@ -28,15 +27,14 @@ import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.search.providers.SearchEngine;
import org.mozilla.search.providers.SearchEngineManager;
public class PostSearchFragment extends Fragment {
private static final String LOG_TAG = "PostSearchFragment";
private ProgressBar progressBar;
private SearchEngine engine;
private SearchEngineManager searchEngineManager;
private ProgressBar progressBar;
private WebView webview;
private View errorView;
@ -66,33 +64,17 @@ public class PostSearchFragment extends Fragment {
progressBar = null;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
searchEngineManager = new SearchEngineManager(activity);
public void startSearch(SearchEngine engine, String query) {
this.engine = engine;
final String url = engine.resultsUriForQuery(query);
// Only load urls if the url is different than the webview's current url.
if (!TextUtils.equals(webview.getUrl(), url)) {
webview.loadUrl(Constants.ABOUT_BLANK);
webview.loadUrl(url);
}
}
@Override
public void onDetach() {
super.onDetach();
searchEngineManager.destroy();
searchEngineManager = null;
}
public void startSearch(final String query) {
searchEngineManager.getEngine(new SearchEngineManager.SearchEngineCallback() {
@Override
public void execute(SearchEngine engine) {
final String url = engine.resultsUriForQuery(query);
// Load about:blank to avoid flashing old results.
webview.loadUrl(Constants.ABOUT_BLANK);
webview.loadUrl(url);
}
});
}
/**
* A custom WebViewClient that intercepts every page load. This allows
* us to decide whether to load the url here, or send it to Android
@ -108,28 +90,23 @@ public class PostSearchFragment extends Fragment {
// Reset the error state.
networkError = false;
searchEngineManager.getEngine(new SearchEngineManager.SearchEngineCallback() {
@Override
public void execute(SearchEngine engine) {
// We keep URLs in the webview that are either about:blank or a search engine result page.
if (TextUtils.equals(url, Constants.ABOUT_BLANK) || engine.isSearchResultsPage(url)) {
// Keeping the URL in the webview is a noop.
return;
}
// We keep URLs in the webview that are either about:blank or a search engine result page.
if (TextUtils.equals(url, Constants.ABOUT_BLANK) || engine.isSearchResultsPage(url)) {
// Keeping the URL in the webview is a noop.
return;
}
webview.stopLoading();
webview.stopLoading();
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL,
TelemetryContract.Method.CONTENT, "search-result");
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL,
TelemetryContract.Method.CONTENT, "search-result");
final Intent i = new Intent(Intent.ACTION_VIEW);
final Intent i = new Intent(Intent.ACTION_VIEW);
// This sends the URL directly to fennec, rather than to Android.
i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS_NAME);
i.setData(Uri.parse(url));
startActivity(i);
}
});
// This sends the URL directly to fennec, rather than to Android.
i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS_NAME);
i.setData(Uri.parse(url));
startActivity(i);
}
@Override
@ -179,13 +156,7 @@ public class PostSearchFragment extends Fragment {
@Override
public void onReceivedTitle(final WebView view, String title) {
searchEngineManager.getEngine(new SearchEngineManager.SearchEngineCallback() {
@Override
public void execute(SearchEngine engine) {
view.loadUrl(engine.getInjectableJs());
}
});
view.loadUrl(engine.getInjectableJs());
}
@Override

Просмотреть файл

@ -29,7 +29,6 @@ import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
import org.mozilla.search.Constants;
import org.mozilla.search.R;
import org.mozilla.search.providers.SearchEngine;
import org.mozilla.search.providers.SearchEngineManager;
import java.util.ArrayList;
import java.util.List;
@ -37,7 +36,7 @@ import java.util.List;
/**
* A fragment to show search suggestions.
*/
public class SuggestionsFragment extends Fragment implements SearchEngineManager.SearchEngineCallback {
public class SuggestionsFragment extends Fragment {
private static final String LOG_TAG = "SuggestionsFragment";
@ -54,8 +53,6 @@ public class SuggestionsFragment extends Fragment implements SearchEngineManager
private AcceptsSearchQuery searchListener;
private SearchEngineManager searchEngineManager;
// Suggest client gets setup outside of the normal fragment lifecycle, therefore
// clients should ensure that this isn't null before using it.
private SuggestClient suggestClient;
@ -82,18 +79,6 @@ public class SuggestionsFragment extends Fragment implements SearchEngineManager
suggestionLoaderCallbacks = new SuggestionLoaderCallbacks();
autoCompleteAdapter = new AutoCompleteAdapter(activity);
searchEngineManager = new SearchEngineManager(activity);
searchEngineManager.setChangeCallback(this);
// Initialize the suggest client. This may happen asynchronously, so any clients should
// still perform a null check.
searchEngineManager.getEngine(new SearchEngineManager.SearchEngineCallback() {
@Override
public void execute(SearchEngine engine) {
suggestClient = new SuggestClient(getActivity(), engine.getSuggestionTemplate(GECKO_SEARCH_TERMS_URL_PARAM),
SUGGESTION_TIMEOUT, Constants.SUGGESTION_MAX);
}
});
}
@Override
@ -103,8 +88,6 @@ public class SuggestionsFragment extends Fragment implements SearchEngineManager
searchListener = null;
suggestionLoaderCallbacks = null;
autoCompleteAdapter = null;
searchEngineManager.destroy();
searchEngineManager = null;
suggestClient = null;
}
@ -149,8 +132,7 @@ public class SuggestionsFragment extends Fragment implements SearchEngineManager
}
}
@Override
public void execute(SearchEngine engine) {
public void setEngine(SearchEngine engine) {
suggestClient = new SuggestClient(getActivity(), engine.getSuggestionTemplate(GECKO_SEARCH_TERMS_URL_PARAM),
SUGGESTION_TIMEOUT, Constants.SUGGESTION_MAX);
}

Просмотреть файл

@ -137,7 +137,8 @@ DataReportingService.prototype = Object.freeze({
this._os.removeObserver(this, "sessionstore-windows-restored");
this._os.addObserver(this, "quit-application", false);
this.policy.startPolling();
let policy = this.policy;
policy.startPolling();
// Don't initialize Firefox Health Reporter collection and submission
// service unless it is enabled.
@ -175,7 +176,7 @@ DataReportingService.prototype = Object.freeze({
// The instance installs its own shutdown observers. So, we just
// fire and forget: it will clean itself up.
let reporter = this.healthReporter;
this.policy.ensureUserNotified();
policy.ensureUserNotified();
}.bind(this),
}, delayInterval, this.timer.TYPE_ONE_SHOT);

Просмотреть файл

@ -51,7 +51,7 @@ function addDirToZip(writer, dir, basePath) {
* Convert an XPConnect result code to its name and message.
* We have to extract them from an exception per bug 637307 comment 5.
*/
function getResultTest(code) {
function getResultText(code) {
let regexp =
/^\[Exception... "(.*)" nsresult: "0x[0-9a-fA-F]* \((.*)\)" location: ".*" data: .*\]$/;
let ex = Cc["@mozilla.org/js/xpc/Exception;1"].

Просмотреть файл

@ -5077,14 +5077,17 @@ ThreadSources.prototype = {
return this._sourceMapsByGeneratedSource[url]
.then((aSourceMap) => {
let { source: aSourceURL, line: aLine, column: aColumn } = aSourceMap.originalPositionFor({
line: line,
column: column
});
let {
source: aSourceURL,
line: aLine,
column: aColumn,
name: aName
} = aSourceMap.originalPositionFor({ line, column });
return {
url: aSourceURL,
line: aLine,
column: aColumn
column: aColumn,
name: aName
};
})
.then(null, error => {

Просмотреть файл

@ -9,6 +9,9 @@ const { DebuggerServer } = require("devtools/server/main");
const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
const Debugger = require("Debugger");
const { getOffsetColumn } = require("devtools/server/actors/common");
const promise = require("promise");
Cu.import("resource://gre/modules/Task.jsm");
// TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
// once it is implemented.
@ -84,6 +87,7 @@ function TracerActor(aConn, aParent)
this._bufferSendTimer = null;
this._buffer = [];
this._hitCounts = new WeakMap();
this._packetScheduler = new JobScheduler();
// Keep track of how many different trace requests have requested what kind of
// tracing info. This way we can minimize the amount of data we are collecting
@ -265,89 +269,109 @@ TracerActor.prototype = {
* The stack frame that was entered.
*/
onEnterFrame: function(aFrame) {
if (aFrame.script && aFrame.script.url == "self-hosted") {
return;
}
Task.spawn(function*() {
if (aFrame.script && aFrame.script.url == "self-hosted") {
return;
}
let packet = {
type: "enteredFrame",
sequence: this._sequence++
};
// This function might request original (i.e. source-mapped) location,
// which is asynchronous. We need to ensure that packets are sent out
// in the correct order.
let runInOrder = this._packetScheduler.schedule();
if (this._requestsForTraceType.name) {
packet.name = aFrame.callee
? aFrame.callee.displayName || "(anonymous function)"
: "(" + aFrame.type + ")";
}
let packet = {
type: "enteredFrame",
sequence: this._sequence++
};
if (aFrame.script) {
if (this._requestsForTraceType.hitCount) {
// Increment hit count.
let previousHitCount = this._hitCounts.get(aFrame.script) || 0;
this._hitCounts.set(aFrame.script, previousHitCount + 1);
let sourceMappedLocation;
if (this._requestsForTraceType.name || this._requestsForTraceType.location) {
if (aFrame.script) {
sourceMappedLocation = yield this._parent.threadActor.sources.getOriginalLocation({
url: aFrame.script.url,
line: aFrame.script.startLine,
// We should return the location of the start of the script, but
// Debugger.Script does not provide complete start locations (bug
// 901138). Instead, return the current offset (the location of the
// first statement in the function).
column: getOffsetColumn(aFrame.offset, aFrame.script)
});
}
}
packet.hitCount = this._hitCounts.get(aFrame.script);
if (this._requestsForTraceType.name) {
if (sourceMappedLocation && sourceMappedLocation.name) {
packet.name = sourceMappedLocation.name;
} else {
packet.name = aFrame.callee
? aFrame.callee.displayName || "(anonymous function)"
: "(" + aFrame.type + ")";
}
}
if (this._requestsForTraceType.location) {
// We should return the location of the start of the script, but
// Debugger.Script does not provide complete start locations (bug
// 901138). Instead, return the current offset (the location of the first
// statement in the function).
packet.location = {
url: aFrame.script.url,
line: aFrame.script.startLine,
column: getOffsetColumn(aFrame.offset, aFrame.script)
};
}
}
if (this._parent.threadActor && aFrame.script) {
packet.blackBoxed = this._parent.threadActor.sources.isBlackBoxed(aFrame.script.url);
} else {
packet.blackBoxed = false;
}
if (this._requestsForTraceType.callsite
&& aFrame.older
&& aFrame.older.script) {
let older = aFrame.older;
packet.callsite = {
url: older.script.url,
line: older.script.getOffsetLine(older.offset),
column: getOffsetColumn(older.offset, older.script)
};
}
if (this._requestsForTraceType.time) {
packet.time = Date.now() - this._startTime;
}
if (this._requestsForTraceType.parameterNames && aFrame.callee) {
packet.parameterNames = aFrame.callee.parameterNames;
}
if (this._requestsForTraceType.arguments && aFrame.arguments) {
packet.arguments = [];
let i = 0;
for (let arg of aFrame.arguments) {
if (i++ > MAX_ARGUMENTS) {
break;
if (sourceMappedLocation && sourceMappedLocation.url) {
packet.location = sourceMappedLocation;
}
packet.arguments.push(createValueSnapshot(arg, true));
}
}
if (this._requestsForTraceType.depth) {
packet.depth = aFrame.depth;
}
if (this._requestsForTraceType.hitCount) {
if (aFrame.script) {
// Increment hit count.
let previousHitCount = this._hitCounts.get(aFrame.script) || 0;
this._hitCounts.set(aFrame.script, previousHitCount + 1);
const onExitFrame = this.onExitFrame;
aFrame.onPop = function (aCompletion) {
onExitFrame(this, aCompletion);
};
packet.hitCount = this._hitCounts.get(aFrame.script);
}
}
this._send(packet);
if (this._parent.threadActor && aFrame.script) {
packet.blackBoxed = this._parent.threadActor.sources.isBlackBoxed(aFrame.script.url);
} else {
packet.blackBoxed = false;
}
if (this._requestsForTraceType.callsite) {
if (aFrame.older && aFrame.older.script) {
let older = aFrame.older;
packet.callsite = {
url: older.script.url,
line: older.script.getOffsetLine(older.offset),
column: getOffsetColumn(older.offset, older.script)
};
}
}
if (this._requestsForTraceType.time) {
packet.time = Date.now() - this._startTime;
}
if (this._requestsForTraceType.parameterNames && aFrame.callee) {
packet.parameterNames = aFrame.callee.parameterNames;
}
if (this._requestsForTraceType.arguments && aFrame.arguments) {
packet.arguments = [];
let i = 0;
for (let arg of aFrame.arguments) {
if (i++ > MAX_ARGUMENTS) {
break;
}
packet.arguments.push(createValueSnapshot(arg, true));
}
}
if (this._requestsForTraceType.depth) {
packet.depth = aFrame.depth;
}
const onExitFrame = this.onExitFrame;
aFrame.onPop = function (aCompletion) {
onExitFrame(this, aCompletion);
};
runInOrder(() => this._send(packet));
}.bind(this));
},
/**
@ -360,6 +384,8 @@ TracerActor.prototype = {
* The debugger completion value for the frame.
*/
onExitFrame: function(aFrame, aCompletion) {
let runInOrder = this._packetScheduler.schedule();
let packet = {
type: "exitedFrame",
sequence: this._sequence++,
@ -397,7 +423,7 @@ TracerActor.prototype = {
}
}
this._send(packet);
runInOrder(() => this._send(packet));
}
};
@ -655,3 +681,34 @@ function propertySnapshot(aName, aObject) {
value: createValueSnapshot(desc.value)
};
}
/**
* Scheduler for jobs to be run in the same order as in which they were
* scheduled.
*/
function JobScheduler()
{
this._lastScheduledJob = promise.resolve();
}
JobScheduler.prototype = {
/**
* Schedule a new job.
*
* @return A function that can be called anytime with a job as a parameter.
* Job won't be run until all previously scheduled jobs were run.
*/
schedule: function() {
let deferred = promise.defer();
let previousJob = this._lastScheduledJob;
this._lastScheduledJob = deferred.promise;
return function runInOrder(aJob) {
previousJob.then(() => {
aJob();
deferred.resolve();
});
};
}
};
exports.JobScheduler = JobScheduler;

Просмотреть файл

@ -0,0 +1,128 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check that tracer links to a correct (source-mapped) location.
*/
var gDebuggee;
var gClient;
var gTraceClient;
var gThreadClient;
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test() {
initTestTracerServer();
gDebuggee = addTestGlobal("test-source-map");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestThread(gClient, "test-source-map",
function(aResponse, aTabClient, aThreadClient, aTabResponse) {
gThreadClient = aThreadClient;
gThreadClient.resume(function (aResponse) {
gClient.attachTracer(aTabResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
testSourceMaps();
});
});
});
});
do_test_pending();
}
const testSourceMaps = Task.async(function* () {
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
checkTrace(t);
}
tracesStopped.resolve();
});
yield startTrace();
yield executeOnNextTickAndWaitForPause(evalCode, gClient);
yield resume(gThreadClient);
yield tracesStopped.promise;
yield stopTrace();
finishClient(gClient);
});
function startTrace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function evalCode()
{
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(10, 0, "a.js", "function fnOuter() {\n"),
new SourceNode(20, 0, "a.js", " fnInner();\n"),
new SourceNode(30, 0, "a.js", "}\n"),
new SourceNode(10, 0, "b.js", "function fnInner() {\n"),
new SourceNode(20, 0, "b.js", " [1].forEach(function noop() {\n"),
new SourceNode(30, 0, "b.js", " debugger;\n"),
new SourceNode(40, 0, "b.js", " });\n"),
new SourceNode(50, 0, "b.js", "}\n"),
new SourceNode(60, 0, "b.js", "fnOuter();\n"),
])).toStringWithSourceMap({
file: "abc.js",
sourceRoot: "http://example.com/"
});
code += "//# sourceMappingURL=data:text/json," + map.toString();
Components.utils.evalInSandbox(code, gDebuggee, "1.8", "http://example.com/abc.js", 1);
}
function checkTrace({ type, sequence, depth, name, location, blackBoxed })
{
switch(sequence) {
// First packet comes from evalInSandbox in evalCode.
case 0:
do_check_eq(name, "(global)");
break;
case 1:
do_check_eq(name, "fnOuter");
do_check_eq(location.url, "http://example.com/a.js");
do_check_eq(location.line, 10);
break;
case 2:
do_check_eq(name, "fnInner");
do_check_eq(location.url, "http://example.com/b.js");
do_check_eq(location.line, 10);
break;
case 3:
do_check_eq(name, "noop");
do_check_eq(location.url, "http://example.com/b.js");
do_check_eq(location.line, 20);
break;
case 4:
case 5:
case 6:
case 7:
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}
function stopTrace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}

Просмотреть файл

@ -0,0 +1,159 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Create 2 sources, A and B, B is source-mapped. When calling functions A->B->A,
* verify that traces are in a proper order.
*/
var gDebuggee;
var gClient;
var gTraceClient;
var gThreadClient;
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestThread(gClient, "test-tracer-actor",
function(aResponse, aTabClient, aThreadClient, aTabResponse) {
gThreadClient = aThreadClient;
gThreadClient.resume(function (aResponse) {
gClient.attachTracer(aTabResponse.traceActor,
function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
testTraces();
});
});
});
});
do_test_pending();
}
const testTraces = Task.async(function* () {
// Read traces
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
yield startTrace();
yield executeOnNextTickAndWaitForPause(evalSourceMapSetup, gClient);
yield resume(gThreadClient);
evalSetup();
evalTestCode();
yield tracesStopped.promise;
yield stopTrace();
finishClient(gClient);
});
function startTrace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null,
function() { deferred.resolve(); });
return deferred.promise;
}
function evalSourceMapSetup() {
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(1, 0, "b_original.js", "" + function fnSourceMapped() {
fnInner();
} + "\n debugger;")
])).toStringWithSourceMap({
file: "b.js",
sourceRoot: "http://example.com/"
});
code += "//# sourceMappingURL=data:text/json," + map.toString();
Components.utils.evalInSandbox(code, gDebuggee, "1.8", "http://example.com/b.js");
}
function evalSetup()
{
Components.utils.evalInSandbox(
"" + function fnOuter() {
fnSourceMapped();
} + "\n" +
"" + function fnInner() {
[1].forEach(function noop() {});
},
gDebuggee,
"1.8",
"http://example.com/a.js",
1
);
}
function evalTestCode()
{
Components.utils.evalInSandbox(
"fnOuter();",
gDebuggee,
"1.8",
"http://example.com/a.js",
1
);
}
function stopTrace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ type, sequence, depth, name, location, blackBoxed })
{
switch(sequence) {
// First two packets come from evalInSandbox in evalSetup
// The third packet comes from evalInSandbox in evalTestCode
case 0:
case 2:
case 4:
do_check_eq(name, "(global)");
do_check_eq(type, "enteredFrame");
break;
case 5:
do_check_eq(name, "fnOuter");
break;
case 6:
do_check_eq(name, "fnSourceMapped");
break;
case 7:
do_check_eq(name, "fnInner");
break;
case 8:
do_check_eq(name, "noop");
break;
case 1: // evalInSandbox
case 3: // evalInSandbox
case 9: // noop
case 10: // fnInner
case 11: // fnSourceMapped
case 12: // fnOuter
case 13: // evalInSandbox
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -0,0 +1,58 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that job scheduler orders the jobs correctly.
*/
let { JobScheduler } = devtools.require("devtools/server/actors/tracer");
function run_test()
{
do_test_pending();
test_in_order();
test_shuffled();
do_timeout(0, do_test_finished);
}
function test_in_order() {
let jobScheduler = new JobScheduler();
let testArray = [0];
let first = jobScheduler.schedule();
let second = jobScheduler.schedule();
let third = jobScheduler.schedule();
first(() => testArray.push(1));
second(() => testArray.push(2));
third(() => testArray.push(3));
do_timeout(0, () => {
do_check_eq(testArray[0], 0);
do_check_eq(testArray[1], 1);
do_check_eq(testArray[2], 2);
do_check_eq(testArray[3], 3);
});
}
function test_shuffled() {
let jobScheduler = new JobScheduler();
let testArray = [0];
let first = jobScheduler.schedule();
let second = jobScheduler.schedule();
let third = jobScheduler.schedule();
third(() => testArray.push(3));
first(() => testArray.push(1));
second(() => testArray.push(2));
do_timeout(0, () => {
do_check_eq(testArray[0], 0);
do_check_eq(testArray[1], 1);
do_check_eq(testArray[2], 2);
do_check_eq(testArray[3], 3);
});
}

Просмотреть файл

@ -139,6 +139,8 @@ reason = bug 820380
[test_sourcemaps-11.js]
[test_sourcemaps-12.js]
[test_sourcemaps-13.js]
[test_sourcemaps-14.js]
[test_sourcemaps-15.js]
[test_objectgrips-01.js]
[test_objectgrips-02.js]
[test_objectgrips-03.js]
@ -199,6 +201,7 @@ reason = bug 820380
[test_trace_actor-10.js]
[test_trace_actor-11.js]
[test_trace_actor-12.js]
[test_trace_actor-13.js]
[test_ignore_caught_exceptions.js]
[test_requestTypes.js]
reason = bug 937197

Просмотреть файл

@ -75,21 +75,19 @@ function part3(aTestPlugin) {
}
function part4() {
let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser);
waitForCondition(condition, () => {
gPluginBrowser.removeEventListener("PluginBindingAttached", part4);
gBrowser.removeCurrentTab();
ok(PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser), "part4: should have a click-to-play notification");
gPluginBrowser.removeEventListener("PluginBindingAttached", part4);
gBrowser.removeCurrentTab();
let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
let alwaysActivateItem = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "always-activate-menuitem");
menu.selectedItem = alwaysActivateItem;
alwaysActivateItem.doCommand();
gBrowser.selectedTab = gBrowser.addTab();
gPluginBrowser = gBrowser.selectedBrowser;
gPluginBrowser.addEventListener("load", part5, true);
gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
}, "part4: should have a click-to-play notification");
let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
let alwaysActivateItem = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "always-activate-menuitem");
menu.selectedItem = alwaysActivateItem;
alwaysActivateItem.doCommand();
gBrowser.selectedTab = gBrowser.addTab();
gPluginBrowser = gBrowser.selectedBrowser;
gPluginBrowser.addEventListener("load", part5, true);
gPluginBrowser.contentWindow.location = gHttpTestRoot + "plugin_test.html";
}
function part5() {
@ -120,22 +118,20 @@ function part6() {
}
function part7() {
let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser);
waitForCondition(condition, () => {
let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
ok(testPlugin, "part7: should have a plugin element in the page");
let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part7: plugin should not be activated");
ok(PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser), "part7: disabled plugins still show a notification");
let testPlugin = gPluginBrowser.contentDocument.getElementById("test");
ok(testPlugin, "part7: should have a plugin element in the page");
let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part7: plugin should not be activated");
gPluginBrowser.removeEventListener("PluginBindingAttached", part7);
gBrowser.removeCurrentTab();
gPluginBrowser.removeEventListener("PluginBindingAttached", part7);
gBrowser.removeCurrentTab();
let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
let details = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "details-btn");
is_element_visible(details, "part7: details link should be visible");
EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
wait_for_view_load(gManagerWindow, part8);
}, "part7: disabled plugins still show a notification");
let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
let details = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "details-btn");
is_element_visible(details, "part7: details link should be visible");
EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
wait_for_view_load(gManagerWindow, part8);
}
function part8() {
@ -184,21 +180,19 @@ function part10() {
}
function part11() {
let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser);
waitForCondition(condition, () => {
gPluginBrowser.removeEventListener("PluginBindingAttached", part11);
gBrowser.removeCurrentTab();
ok(PopupNotifications.getNotification("click-to-play-plugins", gPluginBrowser), "part11: should have a click-to-play notification");
gPluginBrowser.removeEventListener("PluginBindingAttached", part11);
gBrowser.removeCurrentTab();
let pluginTag = getTestPluginTag();
let pluginTag = getTestPluginTag();
// causes appDisabled to be set
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginHard.xml",
function() {
close_manager(gManagerWindow, function() {
open_manager("addons://list/plugin", part12);
});
// causes appDisabled to be set
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginHard.xml",
function() {
close_manager(gManagerWindow, function() {
open_manager("addons://list/plugin", part12);
});
}, "part11: should have a click-to-play notification");
});
}
function part12(aWindow) {