--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -50,17 +50,21 @@ DIRS = profile/extensions
PREF_JS_EXPORTS = $(srcdir)/profile/firefox.js \
$(srcdir)/profile/channel-prefs.js \
$(NULL)
# hardcode en-US for the moment
AB_CD = en-US
-DEFINES += -DAB_CD=$(AB_CD)
+DEFINES += \
+ -DAB_CD=$(AB_CD) \
+ -DDLL_PREFIX=$(DLL_PREFIX) \
+ -DDLL_SUFFIX=$(DLL_SUFFIX) \
+ $(NULL)
APP_VERSION = $(shell cat $(srcdir)/../config/version.txt)
DEFINES += -DAPP_VERSION="$(APP_VERSION)"
APP_UA_NAME = $(shell echo $(MOZ_APP_DISPLAYNAME) | sed -e's/[^A-Za-z]//g')
DEFINES += -DAPP_UA_NAME="$(APP_UA_NAME)"
DIST_FILES = application.ini
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -904,15 +904,29 @@ pref("browser.sessionstore.interval", 60
// Whether to use a panel that looks like an OS X sheet for customization
#ifdef XP_MACOSX
pref("toolbar.customization.usesheet", true);
#else
pref("toolbar.customization.usesheet", false);
#endif
+// Whitelist the test plugin, Flash, Silverlight, and QuickTime
+
+pref("dom.ipc.plugins.enabled.@DLL_PREFIX@nptest@DLL_SUFFIX@", true);
+#ifdef XP_WIN
+pref("dom.ipc.plugins.enabled.npswf32.dll", true);
+pref("dom.ipc.plugins.enabled.npctrl.dll", true);
+pref("dom.ipc.plugins.enabled.npqtplugin.dll", true);
+#endif
+#ifdef XP_UNIX
+pref("dom.ipc.plugins.enabled.libflashplayer.so", true);
+#endif
+
+pref("dom.ipc.plugins.enabled", false);
+
#ifdef XP_WIN
#ifndef WINCE
pref("browser.taskbar.previews.enable", false);
pref("browser.taskbar.previews.max", 20);
pref("browser.taskbar.previews.cachetime", 20);
#endif
#endif
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -129,17 +129,17 @@ var gEditUIVisible = true;
delete this.PluralForm;
return this.PluralForm = val;
});
#ifdef MOZ_CRASHREPORTER
__defineGetter__("gCrashReporter", function() {
delete this.gCrashReporter;
return this.gCrashReporter = Cc["@mozilla.org/xre/app-info;1"].
- getService(Ci.nsICrashReporter);
+ getService(Ci.nsICrashReporter_MOZILLA_1_9_2_BRANCH);
});
#endif
let gInitialPages = [
"about:blank",
"about:privatebrowsing",
"about:sessionrestore"
];
@@ -1095,24 +1095,28 @@ function HandleAppCommandEvent(evt) {
BrowserHome();
break;
default:
break;
}
}
function prepareForStartup() {
+ var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
+
gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
// Note: we need to listen to untrusted events, because the pluginfinder XBL
// binding can't fire trusted ones (runs with page privileges).
gBrowser.addEventListener("PluginNotFound", gMissingPluginInstaller.newMissingPlugin, true, true);
+ gBrowser.addEventListener("PluginCrashed", gMissingPluginInstaller.pluginInstanceCrashed, true, true);
gBrowser.addEventListener("PluginBlocklisted", gMissingPluginInstaller.newMissingPlugin, true, true);
gBrowser.addEventListener("PluginOutdated", gMissingPluginInstaller.newMissingPlugin, true, true);
gBrowser.addEventListener("PluginDisabled", gMissingPluginInstaller.newDisabledPlugin, true, true);
gBrowser.addEventListener("NewPluginInstalled", gMissingPluginInstaller.refreshBrowser, false);
+ os.addObserver(gMissingPluginInstaller, "plugin-crashed", false);
gBrowser.addEventListener("NewTab", BrowserOpenTab, false);
window.addEventListener("AppCommand", HandleAppCommandEvent, true);
var webNavigation;
try {
webNavigation = getWebNavigation();
if (!webNavigation)
throw "no XBL binding for browser";
@@ -1147,17 +1151,16 @@ function prepareForStartup() {
// Manually hook up session and global history for the first browser
// so that we don't have to load global history before bringing up a
// window.
// Wire up session and global history before any possible
// progress notifications for back/forward button updating
webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
.createInstance(Components.interfaces.nsISHistory);
- var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
os.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
// remove the disablehistory attribute so the browser cleans up, as
// though it had done this work itself
gBrowser.browsers[0].removeAttribute("disablehistory");
// enable global history
try {
@@ -1400,16 +1403,17 @@ function BrowserShutdown()
catch(ex) {
Components.utils.reportError(ex);
}
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
os.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
+ os.removeObserver(gMissingPluginInstaller, "plugin-crashed");
try {
gBrowser.removeProgressListener(window.XULBrowserWindow);
gBrowser.removeTabsProgressListener(window.TabsProgressListener);
} catch (ex) {
}
PlacesStarButton.uninit();
@@ -4446,17 +4450,17 @@ nsBrowserAccess.prototype =
QueryInterface : function(aIID)
{
if (aIID.equals(Ci.nsIBrowserDOMWindow) ||
aIID.equals(Ci.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
- openURI : function(aURI, aOpener, aWhere, aContext)
+ openURI : function (aURI, aOpener, aWhere, aContext)
{
var newWindow = null;
var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
if (isExternal && aURI && aURI.schemeIs("chrome")) {
dump("use -chrome command-line option to load external chrome urls\n");
return null;
}
@@ -4517,17 +4521,17 @@ nsBrowserAccess.prototype =
gBrowser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
}
if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
content.focus();
}
return newWindow;
},
- isTabContentWindow : function(aWindow)
+ isTabContentWindow : function (aWindow)
{
return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
}
}
function onViewToolbarsPopupShowing(aEvent)
{
var popup = aEvent.target;
@@ -5934,216 +5938,536 @@ function getPluginInfo(pluginElement)
if (tagMimetype == "") {
tagMimetype = pluginElement.type;
}
}
return {mimetype: tagMimetype, pluginsPage: pluginsPage};
}
-function missingPluginInstaller(){
-}
-
-missingPluginInstaller.prototype.installSinglePlugin = function(aEvent){
- var missingPluginsArray = {};
-
- var pluginInfo = getPluginInfo(aEvent.target);
- missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
-
- if (missingPluginsArray) {
- window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
- "PFSWindow", "chrome,centerscreen,resizable=yes",
- {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
- }
-
- aEvent.stopPropagation();
-}
-
-missingPluginInstaller.prototype.managePlugins = function(aEvent){
- BrowserOpenAddonsMgr("plugins");
- aEvent.stopPropagation();
-}
-
-missingPluginInstaller.prototype.newMissingPlugin = function(aEvent){
- // Since we are expecting also untrusted events, make sure
- // that the target is a plugin
- if (!(aEvent.target instanceof Components.interfaces.nsIObjectLoadingContent))
- return;
-
- // For broken non-object plugin tags, register a click handler so
- // that the user can click the plugin replacement to get the new
- // plugin. Object tags can, and often do, deal with that themselves,
- // so don't stomp on the page developers toes.
-
- if (aEvent.type != "PluginBlocklisted" &&
- aEvent.type != "PluginOutdated" &&
- !(aEvent.target instanceof HTMLObjectElement)) {
- aEvent.target.addEventListener("click",
- gMissingPluginInstaller.installSinglePlugin,
- true);
- }
-
- let hideBarPrefName = aEvent.type == "PluginOutdated" ?
- "plugins.hide_infobar_for_outdated_plugin" :
- "plugins.hide_infobar_for_missing_plugin";
- try {
- if (gPrefService.getBoolPref(hideBarPrefName))
+var gMissingPluginInstaller = {
+
+ get CrashSubmit() {
+ delete this.CrashSubmit;
+ Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
+ return this.CrashSubmit;
+ },
+
+ get crashReportHelpURL() {
+ delete this.crashReportHelpURL;
+ let url = formatURL("app.support.baseURL", true);
+ url += "plugin-crashed";
+ this.crashReportHelpURL = url;
+ return this.crashReportHelpURL;
+ },
+
+ 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);
+ },
+
+ // Callback for user clicking on a missing (unsupported) plugin.
+ installSinglePlugin: function (aEvent) {
+ var missingPluginsArray = {};
+
+ var pluginInfo = getPluginInfo(aEvent.target);
+ missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
+
+ openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
+ "PFSWindow", "chrome,centerscreen,resizable=yes",
+ {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
+ },
+
+ // Callback for user clicking on a disabled plugin
+ managePlugins: function (aEvent) {
+ BrowserOpenAddonsMgr("plugins");
+ },
+
+ // Callback for user clicking "submit a report" link
+ submitReport : function(pluginDumpID, browserDumpID) {
+ // The crash reporter wants a DOM element it can append an IFRAME to,
+ // which it uses to submit a form. Let's just give it gBrowser.
+ this.CrashSubmit.submit(pluginDumpID, gBrowser, null, null);
+ if (browserDumpID)
+ this.CrashSubmit.submit(browserDumpID, gBrowser, null, null);
+ },
+
+ // Callback for user clicking a "reload page" link
+ reloadPage: function (browser) {
+ browser.reload();
+ },
+
+ // Callback for user clicking the help icon
+ openHelpPage: function () {
+ openHelpLink("plugin-crashed", false);
+ },
+
+
+
+ // event listener for missing/blocklisted/outdated plugins.
+ newMissingPlugin: function (aEvent) {
+ // Since we are expecting also untrusted events, make sure
+ // that the target is a plugin
+ if (!(aEvent.target instanceof Ci.nsIObjectLoadingContent))
return;
- } catch (ex) {} // if the pref is missing, treat it as false, which shows the infobar
-
- var browser = gBrowser.getBrowserForDocument(aEvent.target.ownerDocument
- .defaultView.top.document);
- if (!browser.missingPlugins)
- browser.missingPlugins = {};
-
- var pluginInfo = getPluginInfo(aEvent.target);
-
- browser.missingPlugins[pluginInfo.mimetype] = pluginInfo;
-
- var notificationBox = gBrowser.getNotificationBox(browser);
-
- // Should only display one of these warnings per page.
- // In order of priority, they are: outdated > missing > blocklisted
-
- // If there is already an outdated plugin notification then do nothing
- if (notificationBox.getNotificationWithValue("outdated-plugins"))
- return;
- var blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins");
- var missingNotification = notificationBox.getNotificationWithValue("missing-plugins");
- var priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+
+ // For broken non-object plugin tags, register a click handler so
+ // that the user can click the plugin replacement to get the new
+ // plugin. Object tags can, and often do, deal with that themselves,
+ // so don't stomp on the page developers toes.
+
+ if (aEvent.type != "PluginBlocklisted" &&
+ aEvent.type != "PluginOutdated" &&
+ !(aEvent.target instanceof HTMLObjectElement)) {
+ gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "installSinglePlugin");
+ }
+
+ let hideBarPrefName = aEvent.type == "PluginOutdated" ?
+ "plugins.hide_infobar_for_outdated_plugin" :
+ "plugins.hide_infobar_for_missing_plugin";
+ try {
+ if (gPrefService.getBoolPref(hideBarPrefName))
+ return;
+ } catch (ex) {} // if the pref is missing, treat it as false, which shows the infobar
+
+ var browser = gBrowser.getBrowserForDocument(aEvent.target.ownerDocument
+ .defaultView.top.document);
+ if (!browser.missingPlugins)
+ browser.missingPlugins = {};
+
+ var pluginInfo = getPluginInfo(aEvent.target);
+
+ browser.missingPlugins[pluginInfo.mimetype] = pluginInfo;
+
+ var notificationBox = gBrowser.getNotificationBox(browser);
+
+ // Should only display one of these warnings per page.
+ // In order of priority, they are: outdated > missing > blocklisted
+
+ // If there is already an outdated plugin notification then do nothing
+ if (notificationBox.getNotificationWithValue("outdated-plugins"))
+ return;
+ var blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins");
+ var missingNotification = notificationBox.getNotificationWithValue("missing-plugins");
+ var priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+
+ function showBlocklistInfo() {
+ var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+ getService(Ci.nsIURLFormatter);
+ var url = formatter.formatURLPref("extensions.blocklist.detailsURL");
+ gBrowser.loadOneTab(url, {inBackground: false});
+ return true;
+ }
- function showBlocklistInfo() {
- var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
- getService(Ci.nsIURLFormatter);
- var url = formatter.formatURLPref("extensions.blocklist.detailsURL");
- gBrowser.loadOneTab(url, {inBackground: false});
- return true;
- }
-
- function showOutdatedPluginsInfo() {
- gPrefService.setBoolPref("plugins.update.notifyUser", false);
- var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
- getService(Ci.nsIURLFormatter);
- var url = formatter.formatURLPref("plugins.update.url");
- gBrowser.loadOneTab(url, {inBackground: false});
- return true;
- }
+ function showOutdatedPluginsInfo() {
+ gPrefService.setBoolPref("plugins.update.notifyUser", false);
+ var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+ getService(Ci.nsIURLFormatter);
+ var url = formatter.formatURLPref("plugins.update.url");
+ gBrowser.loadOneTab(url, {inBackground: false});
+ return true;
+ }
- function showPluginsMissing() {
- // get the urls of missing plugins
- var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins;
- if (missingPluginsArray) {
- window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
- "PFSWindow", "chrome,centerscreen,resizable=yes",
- {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
- }
- }
-
- if (aEvent.type == "PluginBlocklisted") {
- if (blockedNotification || missingNotification)
+ function showPluginsMissing() {
+ // get the urls of missing plugins
+ var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins;
+ if (missingPluginsArray) {
+ window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
+ "PFSWindow", "chrome,centerscreen,resizable=yes",
+ {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
+ }
+ }
+
+ if (aEvent.type == "PluginBlocklisted") {
+ if (blockedNotification || missingNotification)
+ return;
+
+ let iconURL = "chrome://mozapps/skin/plugins/pluginBlocked-16.png";
+ let messageString = gNavigatorBundle.getString("blockedpluginsMessage.title");
+ let buttons = [{
+ label: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"),
+ accessKey: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"),
+ popup: null,
+ callback: showBlocklistInfo
+ }, {
+ label: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"),
+ accessKey: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"),
+ popup: null,
+ callback: showOutdatedPluginsInfo
+ }];
+
+ notificationBox.appendNotification(messageString, "blocked-plugins",
+ iconURL, priority, buttons);
+ }
+ else if (aEvent.type == "PluginOutdated") {
+ // Cancel any notification about blocklisting/missing plugins
+ if (blockedNotification)
+ blockedNotification.close();
+ if (missingNotification)
+ missingNotification.close();
+
+ let iconURL = "chrome://mozapps/skin/plugins/pluginOutdated-16.png";
+ let messageString = gNavigatorBundle.getString("outdatedpluginsMessage.title");
+ let buttons = [{
+ label: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"),
+ accessKey: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"),
+ popup: null,
+ callback: showOutdatedPluginsInfo
+ }];
+
+ notificationBox.appendNotification(messageString, "outdated-plugins",
+ iconURL, priority, buttons);
+ }
+ else if (aEvent.type == "PluginNotFound") {
+ if (missingNotification)
+ return;
+
+ // Cancel any notification about blocklisting plugins
+ if (blockedNotification)
+ blockedNotification.close();
+
+ let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
+ let messageString = gNavigatorBundle.getString("missingpluginsMessage.title");
+ let buttons = [{
+ label: gNavigatorBundle.getString("missingpluginsMessage.button.label"),
+ accessKey: gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"),
+ popup: null,
+ callback: showPluginsMissing
+ }];
+
+ notificationBox.appendNotification(messageString, "missing-plugins",
+ iconURL, priority, buttons);
+ }
+ },
+
+ newDisabledPlugin: function (aEvent) {
+ // Since we are expecting also untrusted events, make sure
+ // that the target is a plugin
+ if (!(aEvent.target instanceof Components.interfaces.nsIObjectLoadingContent))
+ return;
+
+ gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "managePlugins");
+ },
+
+ // Crashed-plugin observer. Notified once per plugin crash, before events
+ // are dispatched to individual plugin instances.
+ observe: function(subject, topic, data) {
+ if (topic != "plugin-crashed")
+ return;
+
+ let propertyBag = subject;
+ if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
+ !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
+ return;
+
+#ifdef MOZ_CRASHREPORTER
+ let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
+ let browserDumpID= propertyBag.getPropertyAsAString("browserDumpID");
+ let shouldSubmit = gCrashReporter.submitReports;
+ let doPrompt = true; // XXX followup to get via gCrashReporter
+
+ // Submit automatically when appropriate.
+ if (pluginDumpID && shouldSubmit && !doPrompt) {
+ this.submitReport(pluginDumpID, browserDumpID);
+ // Submission is async, so we can't easily show failure UI.
+ propertyBag.setPropertyAsBool("submittedCrashReport", true);
+ }
+#endif
+ },
+
+ // Crashed-plugin event listener. Called for every instance of a
+ // plugin in content.
+ pluginInstanceCrashed: function (aEvent) {
+ let self = gMissingPluginInstaller;
+
+ // Evil content could fire a fake event at us, ignore them.
+ if (!aEvent.isTrusted)
+ return;
+
+ if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent))
+ return;
+
+ let submittedReport = aEvent.getData("submittedCrashReport");
+ let doPrompt = true; // XXX followup for .getData("doPrompt");
+ let submitReports = true; // XXX followup for .getData("submitReports");
+ let pluginName = aEvent.getData("pluginName");
+ let pluginDumpID = aEvent.getData("pluginDumpID");
+ let browserDumpID = aEvent.getData("browserDumpID");
+
+ // We're expecting this to be a plugin.
+ let plugin = aEvent.target;
+ if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return;
- let iconURL = "chrome://mozapps/skin/plugins/pluginBlocked-16.png";
- let messageString = gNavigatorBundle.getString("blockedpluginsMessage.title");
- let buttons = [{
- label: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"),
- accessKey: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"),
- popup: null,
- callback: showBlocklistInfo
- }, {
- label: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"),
- accessKey: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"),
- popup: null,
- callback: showOutdatedPluginsInfo
- }];
-
- notificationBox.appendNotification(messageString, "blocked-plugins",
- iconURL, priority, buttons);
- }
- else if (aEvent.type == "PluginOutdated") {
- // Cancel any notification about blocklisting/missing plugins
- if (blockedNotification)
- blockedNotification.close();
- if (missingNotification)
- missingNotification.close();
-
- let iconURL = "chrome://mozapps/skin/plugins/pluginOutdated-16.png";
- let messageString = gNavigatorBundle.getString("outdatedpluginsMessage.title");
- let buttons = [{
- label: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"),
- accessKey: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"),
- popup: null,
- callback: showOutdatedPluginsInfo
- }];
-
- notificationBox.appendNotification(messageString, "outdated-plugins",
- iconURL, priority, buttons);
- }
- else if (aEvent.type == "PluginNotFound") {
- if (missingNotification)
- return;
-
- // Cancel any notification about blocklisting plugins
- if (blockedNotification)
- blockedNotification.close();
-
- let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
- let messageString = gNavigatorBundle.getString("missingpluginsMessage.title");
- let buttons = [{
- label: gNavigatorBundle.getString("missingpluginsMessage.button.label"),
- accessKey: gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"),
- popup: null,
- callback: showPluginsMissing
- }];
-
- notificationBox.appendNotification(messageString, "missing-plugins",
- iconURL, priority, buttons);
- }
-}
-
-missingPluginInstaller.prototype.newDisabledPlugin = function(aEvent){
- // Since we are expecting also untrusted events, make sure
- // that the target is a plugin
- if (!(aEvent.target instanceof Components.interfaces.nsIObjectLoadingContent))
- return;
-
- aEvent.target.addEventListener("click",
- gMissingPluginInstaller.managePlugins,
- true);
-}
-
-missingPluginInstaller.prototype.refreshBrowser = function(aEvent) {
- // browser elements are anonymous so we can't just use target.
- var browser = aEvent.originalTarget;
- var notificationBox = gBrowser.getNotificationBox(browser);
- var notification = notificationBox.getNotificationWithValue("missing-plugins");
-
- // clear the plugin list, now that at least one plugin has been installed
- browser.missingPlugins = null;
- if (notification) {
- // reset UI
- notificationBox.removeNotification(notification);
- }
- // reload the browser to make the new plugin show.
- browser.reload();
-}
-
-var gMissingPluginInstaller = new missingPluginInstaller();
+ // Force a style flush, so that we ensure our binding is attached.
+ plugin.clientTop;
+
+ let messageString;
+ try {
+ messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
+ } catch (e) {
+ messageString = "The " + pluginName + " plugin has crashed.";
+ }
+
+ //
+ // Configure the crashed-plugin placeholder.
+ //
+ let doc = plugin.ownerDocument;
+ let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+
+ // The binding has role="link" here, since missing/disabled/blocked
+ // plugin UI has a onclick handler on the whole thing. This isn't needed
+ // for the plugin-crashed UI, because we use actual HTML links in the text.
+ overlay.removeAttribute("role");
+
+#ifdef MOZ_CRASHREPORTER
+ // Determine which message to show regarding crash reports.
+ let helpClass, showClass;
+ if (submittedReport) { // submitReports && !doPrompt, handled in observer
+ showClass = "msg msgSubmitted";
+ }
+ else if (!submitReports && !doPrompt) {
+ showClass = "msg msgNotSubmitted";
+ }
+ else { // doPrompt
+ showClass = "msg msgPleaseSubmit";
+ // XXX can we make the link target actually be blank?
+ let pleaseLink = doc.getAnonymousElementByAttribute(
+ plugin, "class", "pleaseSubmitLink");
+ self.addLinkClickCallback(pleaseLink, "submitReport",
+ pluginDumpID, browserDumpID);
+ }
+
+ // 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) {
+ showClass = "msg msgNoCrashReport";
+ }
+
+ let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
+ textToShow.style.display = "block";
+
+ let bottomLinks = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgBottomLinks");
+ bottomLinks.style.display = "block";
+ let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
+ self.addLinkClickCallback(helpIcon, "openHelpPage");
+
+ // 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 : function(subject, topic, data) {
+ let propertyBag = subject;
+ if (!(propertyBag instanceof Ci.nsIPropertyBag2))
+ return;
+ // Ignore notifications for other crashes.
+ if (propertyBag.get("minidumpID") != pluginDumpID)
+ return;
+ self.updateSubmissionStatus(plugin, propertyBag, data);
+ },
+
+ handleEvent : function(event) {
+ // Not expected to be called, just here for the closure.
+ }
+ }
+
+ let obs = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ // Use a weak reference, so we don't have to remove it...
+ 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 crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
+ crashText.textContent = messageString;
+
+ let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
+
+ let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
+ self.addLinkClickCallback(link, "reloadPage", browser);
+
+ let notificationBox = gBrowser.getNotificationBox(browser);
+
+ // 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 isObjectTooSmall = (overlay.scrollWidth > pluginRect.width) ||
+ (overlay.scrollHeight - 5 > pluginRect.height);
+ if (isObjectTooSmall) {
+ // Hide the overlay's contents. Use visibility style, so that it
+ // doesn't collapse down to 0x0.
+ overlay.style.visibility = "hidden";
+ // 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)
+ showNotificationBar(pluginDumpID, browserDumpID);
+ } else {
+ // 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.
+ hideNotificationBar();
+ doc.mozNoPluginCrashedNotification = true;
+ }
+
+ function hideNotificationBar() {
+ let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+ if (notification)
+ notificationBox.removeNotification(notification, true);
+ }
+
+ function showNotificationBar(pluginDumpID, browserDumpID) {
+ // If there's already an existing notification bar, don't do anything.
+ let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+ if (notification)
+ return;
+
+ // Configure the notification bar
+ let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+ let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
+
+ let reloadLabel, reloadKey, submitLabel, submitKey;
+ try {
+ reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
+ reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
+ submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
+ submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
+ } catch (e) {
+ reloadLabel = "Reload page";
+ reloadKey = "R";
+ submitLabel = "Submit a crash report";
+ submitKey = "S";
+ }
+
+ let buttons = [{
+ label: reloadLabel,
+ accessKey: reloadKey,
+ popup: null,
+ callback: function() { browser.reload(); },
+ }];
+#ifdef MOZ_CRASHREPORTER
+ let submitButton = {
+ label: submitLabel,
+ accessKey: submitKey,
+ popup: null,
+ callback: function() { gMissingPluginInstaller.submitReport(pluginDumpID, browserDumpID); },
+ };
+ if (minidumpID)
+ buttons.push(submitButton);
+#endif
+
+ let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
+ iconURL, priority, buttons);
+
+ // Add the "learn more" link.
+ let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ let link = notification.ownerDocument.createElementNS(XULNS, "label");
+ link.className = "text-link";
+ let learnMore;
+ try {
+ learnMore = gNavigatorBundle.getString("crashedpluginsMessage.learnMore");
+ } catch (e) {
+ learnMore = "Learn More\u2026";
+ }
+ link.setAttribute("value", learnMore);
+ link.href = gMissingPluginInstaller.crashReportHelpURL;
+ let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
+ description.appendChild(link);
+ }
+
+ },
+
+ updateSubmissionStatus : function (plugin, propBag, status) {
+ let doc = plugin.ownerDocument;
+
+ // One of these two may already be visible, reset them to be hidden.
+ let pleaseText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgPleaseSubmit");
+ let submittingText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgSubmitting");
+ pleaseText.style.display = "";
+ submittingText.style.display = "";
+
+ let msgClass;
+ switch (status) {
+ case "submitting":
+ msgClass = "msg msgSubmitting";
+ break;
+ case "success":
+ msgClass = "msg msgSubmitted";
+ break;
+ case "failed":
+ msgClass = "msg msgSubmitFailed";
+ break;
+ }
+
+ let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", msgClass);
+ textToShow.style.display = "block";
+ },
+
+ refreshBrowser: function (aEvent) {
+ // browser elements are anonymous so we can't just use target.
+ var browser = aEvent.originalTarget;
+ var notificationBox = gBrowser.getNotificationBox(browser);
+ var notification = notificationBox.getNotificationWithValue("missing-plugins");
+
+ // clear the plugin list, now that at least one plugin has been installed
+ browser.missingPlugins = null;
+ if (notification) {
+ // reset UI
+ notificationBox.removeNotification(notification);
+ }
+ // reload the browser to make the new plugin show.
+ browser.reload();
+ }
+};
function convertFromUnicode(charset, str)
{
try {
var unicodeConverter = Components
.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
unicodeConverter.charset = charset;
str = unicodeConverter.ConvertFromUnicode(str);
return str + unicodeConverter.Finish();
} catch(ex) {
- return null;
+ return null;
}
}
/**
* The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
* and shows UI when they are discovered.
*/
var FeedHandler = {
--- a/browser/branding/unofficial/branding.nsi
+++ b/browser/branding/unofficial/branding.nsi
@@ -35,13 +35,13 @@
# ***** END LICENSE BLOCK *****
# NSIS branding defines for unofficial builds.
# The official release build branding.nsi is located in other-license/branding/firefox/
# The nightly build branding.nsi is located in browser/installer/windows/nsis/
# BrandFullNameInternal is used for some registry and file system values
# instead of BrandFullName and typically should not be modified.
-!define BrandFullNameInternal "Namoroka"
+!define BrandFullNameInternal "Lorentz"
!define CompanyName "mozilla.org"
!define URLInfoAbout "http://www.mozilla.org"
!define URLUpdateInfo "http://www.mozilla.org/projects/firefox"
!define SurveyURL "https://survey.mozilla.com/1/Mozilla%20Firefox/${AppVersion}/${AB_CD}/exit.html"
--- a/browser/branding/unofficial/configure.sh
+++ b/browser/branding/unofficial/configure.sh
@@ -1,1 +1,1 @@
-MOZ_APP_DISPLAYNAME="Namoroka"
+MOZ_APP_DISPLAYNAME="Lorentz"
--- a/browser/branding/unofficial/locales/browserconfig.properties
+++ b/browser/branding/unofficial/locales/browserconfig.properties
@@ -1,3 +1,3 @@
# Do NOT localize or otherwise change these values
-browser.startup.homepage=http://www.mozilla.org/projects/namoroka/
+browser.startup.homepage=http://www.mozilla.org/projects/lorentz/
--- a/browser/branding/unofficial/locales/en-US/brand.dtd
+++ b/browser/branding/unofficial/locales/en-US/brand.dtd
@@ -1,7 +1,7 @@
-<!ENTITY brandShortName "Namoroka">
-<!ENTITY brandFullName "Namoroka">
+<!ENTITY brandShortName "Lorentz">
+<!ENTITY brandFullName "Lorentz">
<!ENTITY vendorShortName "mozilla.org">
<!ENTITY logoCopyright " ">
<!-- LOCALIZATION NOTE (releaseBaseURL): The about: page appends __MOZ_APP_VERSION__.html, e.g. 2.0.html -->
-<!ENTITY releaseBaseURL "http://www.mozilla.org/projects/namoroka/releases/">
+<!ENTITY releaseBaseURL "http://www.mozilla.org/projects/lorentz/releases/">
--- a/browser/branding/unofficial/locales/en-US/brand.properties
+++ b/browser/branding/unofficial/locales/en-US/brand.properties
@@ -1,3 +1,3 @@
-brandShortName=Namoroka
-brandFullName=Namoroka
+brandShortName=Lorentz
+brandFullName=Lorentz
vendorShortName=mozilla.org
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -61,16 +61,19 @@ var gAdvancedPane = {
}
#ifdef MOZ_UPDATER
this.updateAppUpdateItems();
this.updateAutoItems();
this.updateModeItems();
#endif
this.updateOfflineApps();
+#ifdef MOZ_CRASHREPORTER
+ this.initSubmitCrashes();
+#endif
},
/**
* Stores the identity of the current tab in preferences so that the selected
* tab can be persisted between openings of the preferences window.
*/
tabSelectionChanged: function ()
{
@@ -134,16 +137,45 @@ var gAdvancedPane = {
* unchanged and represents a value not strictly allowed in UI.
*/
writeCheckSpelling: function ()
{
var checkbox = document.getElementById("checkSpelling");
return checkbox.checked ? (this._storedSpellCheck == 2 ? 2 : 1) : 0;
},
+ /**
+ *
+ */
+ initSubmitCrashes: function ()
+ {
+ var checkbox = document.getElementById("submitCrashesBox");
+ try {
+ var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
+ getService(Components.interfaces.nsICrashReporter_MOZILLA_1_9_2_BRANCH);
+ checkbox.checked = cr.submitReports;
+ } catch (e) {
+ checkbox.style.display = "none";
+ }
+ },
+
+ /**
+ *
+ */
+ updateSubmitCrashes: function ()
+ {
+ var checkbox = document.getElementById("submitCrashesBox");
+ try {
+ var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
+ getService(Components.interfaces.nsICrashReporter);
+ cr.submitReports = checkbox.checked;
+ } catch (e) { }
+ },
+
+
// NETWORK TAB
/*
* Preferences:
*
* browser.cache.disk.capacity
* - the size of the browser cache in KB
*/
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -42,16 +42,18 @@
<!DOCTYPE overlay [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % advancedDTD SYSTEM "chrome://browser/locale/preferences/advanced.dtd">
%advancedDTD;
<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
%privacyDTD;
+<!ENTITY submitCrashes.label "Submit crash reports">
+<!ENTITY submitCrashes.accesskey "S">
]>
<overlay id="AdvancedPaneOverlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<prefpane id="paneAdvanced" onpaneload="gAdvancedPane.init();">
<preferences id="advancedPreferences">
@@ -179,28 +181,33 @@
accesskey="&checkSpelling.accesskey;"
onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
preference="layout.spellcheckDefault"/>
</groupbox>
#ifdef HAVE_SHELL_SERVICE
<!-- System Defaults -->
- <groupbox id="systemDefaultsGroup" orient="horizontal">
+ <groupbox id="systemDefaultsGroup" orient="vertical">
<caption label="&systemDefaults.label;"/>
<hbox id="checkDefaultBox" align="center" flex="1">
<checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
flex="1"/>
<button id="checkDefaultButton"
label="&checkNow.label;" accesskey="&checkNow.accesskey;"
oncommand="gAdvancedPane.checkNow()"
preference="pref.general.disable_button.default_browser"/>
</hbox>
+#ifdef MOZ_CRASHREPORTER
+ <checkbox id="submitCrashesBox" flex="1"
+ oncommand="gAdvancedPane.updateSubmitCrashes();"
+ label="&submitCrashes.label;" accesskey="&submitCrashes.accesskey;"/>
+#endif
</groupbox>
#endif
</tabpanel>
<!-- Network -->
<tabpanel id="networkPanel" orient="vertical">
<!-- Connection -->
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -49,26 +49,30 @@
@BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@xpcom@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
#ifdef XP_MACOSX
@BINPATH@/XUL
#else
@BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
#endif
+#ifdef MOZ_IPC
+@BINPATH@/mozilla-runtime@BIN_SUFFIX@
+#endif
#ifdef WINCE
@BINPATH@/mozce_shunt.dll
#elifdef XP_WIN32
#ifndef MOZ_MEMORY
@BINPATH@/Microsoft.VC80.CRT.manifest
@BINPATH@/msvcm80.dll
@BINPATH@/msvcp80.dll
@BINPATH@/msvcr80.dll
#else
@BINPATH@/mozcrt19.dll
+@BINPATH@/mozcpp19.dll
#endif
#endif
[browser]
; [Base Browser Files]
#ifdef XP_WIN32
@BINPATH@/@MOZ_APP_NAME@.exe
#else
@@ -345,16 +349,17 @@
@BINPATH@/components/nsBadCertHandler.js
@BINPATH@/components/nsFormAutoComplete.js
#ifdef XP_MACOSX
@BINPATH@/components/libalerts_s.dylib
#endif
#ifdef MOZ_ENABLE_DBUS
@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
#endif
+@BINPATH@/components/nsINIProcessor.js
; Modules
@BINPATH@/modules/*
; Safe Browsing
@BINPATH@/components/nsSafebrowsingApplication.js
@BINPATH@/components/nsUrlClassifierListManager.js
@BINPATH@/components/nsUrlClassifierLib.js
--- a/browser/locales/filter.py
+++ b/browser/locales/filter.py
@@ -1,42 +1,30 @@
def test(mod, path, entity = None):
import re
# ignore anyhting but Firefox
if mod not in ("netwerk", "dom", "toolkit", "security/manager",
"browser", "extensions/reporter", "extensions/spellcheck",
"other-licenses/branding/firefox"):
return False
-
- # Ignore Lorentz strings, at least temporarily
- if mod == "toolkit" and path == "chrome/mozapps/plugins/plugins.dtd":
- if entity.startswith('reloadPlugin.'): return False
- if entity.startswith('report.'): return False
-
if mod != "browser" and mod != "extensions/spellcheck":
# we only have exceptions for browser and extensions/spellcheck
return True
if not entity:
if mod == "extensions/spellcheck":
return False
# browser
return not (re.match(r"searchplugins\/.+\.xml", path) or
re.match(r"chrome\/help\/images\/[A-Za-z-_]+\.png", path))
if mod == "extensions/spellcheck":
# l10n ships en-US dictionary or something, do compare
return True
if path == "defines.inc":
return entity != "MOZ_LANGPACK_CONTRIBUTORS"
- # Ignore Lorentz strings, at least temporarily
- if mod == 'browser' and path == 'chrome/browser/browser.properties':
- if entity.startswith('crashedpluginsMessage.'): return False
- if mod == 'browser' and path == 'chrome/browser/preferences/advanced.dtd':
- if entity.startswith('submitCrashes'): return False
-
if path != "chrome/browser-region/region.properties":
# only region.properties exceptions remain, compare all others
return True
return not (re.match(r"browser\.search\.order\.[1-9]", entity) or
re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or
re.match(r"gecko\.handlerService\.schemes\.", entity) or
re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity))
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -94,18 +94,18 @@ libs:: bloaturls.txt
# Install bloatcycle.html into dist/bin/res, for auto-cycling
# of bloaturls.txt. This is for browsers that can't do -f
# autocycling of URLs.
libs:: bloatcycle.html
$(INSTALL) $< $(DIST)/bin/res
ifeq ($(OS_ARCH),Darwin)
-libs:: $(topsrcdir)/tools/rb/fix-macosx-stack.pl
- $(INSTALL) $< $(DIST)/bin
+libs:: $(topsrcdir)/tools/rb/fix-macosx-stack.pl $(topsrcdir)/tools/rb/fix_macosx_stack.py
+ $(INSTALL) $^ $(DIST)/bin
# Basic unit tests for some stuff in the unify script
check::
# build ppc/i386 binaries, and unify them
rm -f unify-test-ppc unify-test-i386 unify-test-universal
$(HOST_CC) -arch ppc $(srcdir)/unify-test.c -o unify-test-ppc
$(HOST_CC) -arch i386 $(srcdir)/unify-test.c -o unify-test-i386
@if ! $(srcdir)/macosx/universal/unify ./unify-test-ppc ./unify-test-i386 \
--- a/build/automation-build.mk
+++ b/build/automation-build.mk
@@ -1,10 +1,9 @@
-
-ifeq ($(USE_SHORT_LIBNAME), 1)
+ifneq (,$(filter OS2 WINCE WINNT,$(OS_ARCH)))
PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
else
PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
endif
TARGET_DIST = $(TARGET_DEPTH)/dist
ifeq ($(MOZ_BUILD_APP),camino)
@@ -17,28 +16,26 @@ else
browser_path = \"$(TARGET_DIST)/$(MOZ_APP_DISPLAYNAME).app/Contents/MacOS/$(PROGRAM)\"
endif
else
browser_path = \"$(TARGET_DIST)/bin/$(PROGRAM)\"
endif
endif
_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo
-_SYMBOLS_PATH = $(TARGET_DIST)/crashreporter-symbols
ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR))
_CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
AUTOMATION_PPARGS = \
-DBROWSER_PATH=$(browser_path) \
-DXPC_BIN_PATH=\"$(LIBXUL_DIST)/bin\" \
-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
-DPROFILE_DIR=\"$(_PROFILE_DIR)\" \
-DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \
- -DSYMBOLS_PATH=\"$(_SYMBOLS_PATH)\" \
-DPERL="\"$(PERL)\"" \
$(NULL)
ifeq ($(OS_ARCH),Darwin)
AUTOMATION_PPARGS += -DIS_MAC=1
else
AUTOMATION_PPARGS += -DIS_MAC=0
endif
@@ -66,11 +63,19 @@ AUTOMATION_PPARGS += -DIS_TEST_BUILD=0
endif
ifeq ($(MOZ_DEBUG), 1)
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1
else
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0
endif
+ifdef MOZ_CRASHREPORTER
+AUTOMATION_PPARGS += -DCRASHREPORTER=1
+else
+AUTOMATION_PPARGS += -DCRASHREPORTER=0
+endif
+
automation.py: $(MOZILLA_DIR)/build/automation.py.in $(MOZILLA_DIR)/build/automation-build.mk
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
+
+GARBAGE += automation.py
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -33,114 +33,48 @@
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import codecs
-from datetime import datetime
+from datetime import datetime, timedelta
import itertools
import logging
import os
import re
+import select
import shutil
import signal
import subprocess
import sys
import threading
-
-"""
-Runs the browser from a script, and provides useful utilities
-for setting up the browser environment.
-"""
-
-SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
-sys.path.insert(0, SCRIPT_DIR);
-from automationutils import checkForCrashes
-
-__all__ = [
- "UNIXISH",
- "IS_WIN32",
- "IS_MAC",
- "log",
- "runApp",
- "Process",
- "initializeProfile",
- "DIST_BIN",
- "DEFAULT_APP",
- "CERTS_SRC_DIR",
- "environment",
- "IS_TEST_BUILD",
- "IS_DEBUG_BUILD",
- "SYMBOLS_PATH",
- ]
-
-# These are generated in mozilla/build/Makefile.in
-#expand DIST_BIN = __XPC_BIN_PATH__
-#expand IS_WIN32 = len("__WIN32__") != 0
-#expand IS_MAC = __IS_MAC__ != 0
-#expand IS_LINUX = __IS_LINUX__ != 0
-#ifdef IS_CYGWIN
-#expand IS_CYGWIN = __IS_CYGWIN__ == 1
-#else
-IS_CYGWIN = False
-#endif
-#expand IS_CAMINO = __IS_CAMINO__ != 0
-#expand BIN_SUFFIX = __BIN_SUFFIX__
-#expand PERL = __PERL__
-
-UNIXISH = not IS_WIN32 and not IS_MAC
-
-#expand DEFAULT_APP = "./" + __BROWSER_PATH__
-#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__
-#expand IS_TEST_BUILD = __IS_TEST_BUILD__
-#expand IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
-#expand SYMBOLS_PATH = __SYMBOLS_PATH__
-
-###########
-# LOGGING #
-###########
-
-# We use the logging system here primarily because it'll handle multiple
-# threads, which is needed to process the output of the server and application
-# processes simultaneously.
-log = logging.getLogger()
-handler = logging.StreamHandler(sys.stdout)
-log.setLevel(logging.INFO)
-log.addHandler(handler)
+import tempfile
-#################
-# SUBPROCESSING #
-#################
-
-class Process(subprocess.Popen):
- """
- Represents our view of a subprocess.
- It adds a kill() method which allows it to be stopped explicitly.
- """
+#expand _DIST_BIN = __XPC_BIN_PATH__
+#expand _IS_WIN32 = len("__WIN32__") != 0
+#expand _IS_MAC = __IS_MAC__ != 0
+#expand _IS_LINUX = __IS_LINUX__ != 0
+#ifdef IS_CYGWIN
+#expand _IS_CYGWIN = __IS_CYGWIN__ == 1
+#else
+_IS_CYGWIN = False
+#endif
+#expand _IS_CAMINO = __IS_CAMINO__ != 0
+#expand _BIN_SUFFIX = __BIN_SUFFIX__
+#expand _PERL = __PERL__
- def kill(self):
- if IS_WIN32:
- import platform
- pid = "%i" % self.pid
- if platform.release() == "2000":
- # Windows 2000 needs 'kill.exe' from the 'Windows 2000 Resource Kit tools'. (See bug 475455.)
- try:
- subprocess.Popen(["kill", "-f", pid]).wait()
- except:
- log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid)
- else:
- # Windows XP and later.
- subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait()
- else:
- os.kill(self.pid, signal.SIGKILL)
-
+#expand _DEFAULT_APP = "./" + __BROWSER_PATH__
+#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__
+#expand _IS_TEST_BUILD = __IS_TEST_BUILD__
+#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
+#expand _CRASHREPORTER = __CRASHREPORTER__ == 1
#################
# PROFILE SETUP #
#################
class SyntaxError(Exception):
"Signifies a syntax error on a particular line in server-locations.txt."
@@ -161,104 +95,187 @@ class Location:
"Represents a location line in server-locations.txt."
def __init__(self, scheme, host, port, options):
self.scheme = scheme
self.host = host
self.port = port
self.options = options
-
-def readLocations(locationsPath = "server-locations.txt"):
+class Automation(object):
"""
- Reads the locations at which the Mochitest HTTP server is available from
- server-locations.txt.
+ Runs the browser from a script, and provides useful utilities
+ for setting up the browser environment.
"""
- locationFile = codecs.open(locationsPath, "r", "UTF-8")
+ DIST_BIN = _DIST_BIN
+ IS_WIN32 = _IS_WIN32
+ IS_MAC = _IS_MAC
+ IS_LINUX = _IS_LINUX
+ IS_CYGWIN = _IS_CYGWIN
+ IS_CAMINO = _IS_CAMINO
+ BIN_SUFFIX = _BIN_SUFFIX
+ PERL = _PERL
+
+ UNIXISH = not IS_WIN32 and not IS_MAC
+
+ DEFAULT_APP = _DEFAULT_APP
+ CERTS_SRC_DIR = _CERTS_SRC_DIR
+ IS_TEST_BUILD = _IS_TEST_BUILD
+ IS_DEBUG_BUILD = _IS_DEBUG_BUILD
+ CRASHREPORTER = _CRASHREPORTER
+
+ SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
+ sys.path.insert(0, SCRIPT_DIR)
+ automationutils = __import__('automationutils')
+
+ # timeout, in seconds
+ DEFAULT_TIMEOUT = 60.0
+
+ log = logging.getLogger()
+
+ def __init__(self):
+
+ # We use the logging system here primarily because it'll handle multiple
+ # threads, which is needed to process the output of the server and application
+ # processes simultaneously.
+ handler = logging.StreamHandler(sys.stdout)
+ self.log.setLevel(logging.INFO)
+ self.log.addHandler(handler)
- # Perhaps more detail than necessary, but it's the easiest way to make sure
- # we get exactly the format we want. See server-locations.txt for the exact
- # format guaranteed here.
- lineRe = re.compile(r"^(?P<scheme>[a-z][-a-z0-9+.]*)"
+ @property
+ def __all__(self):
+ return [
+ "UNIXISH",
+ "IS_WIN32",
+ "IS_MAC",
+ "log",
+ "runApp",
+ "Process",
+ "addCommonOptions",
+ "initializeProfile",
+ "DIST_BIN",
+ "DEFAULT_APP",
+ "CERTS_SRC_DIR",
+ "environment",
+ "IS_TEST_BUILD",
+ "IS_DEBUG_BUILD",
+ "DEFAULT_TIMEOUT",
+ ]
+
+ class Process(subprocess.Popen):
+ """
+ Represents our view of a subprocess.
+ It adds a kill() method which allows it to be stopped explicitly.
+ """
+
+ def kill(self):
+ if Automation().IS_WIN32:
+ import platform
+ pid = "%i" % self.pid
+ if platform.release() == "2000":
+ # Windows 2000 needs 'kill.exe' from the
+ #'Windows 2000 Resource Kit tools'. (See bug 475455.)
+ try:
+ subprocess.Popen(["kill", "-f", pid]).wait()
+ except:
+ self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid)
+ else:
+ # Windows XP and later.
+ subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait()
+ else:
+ os.kill(self.pid, signal.SIGKILL)
+
+ def readLocations(self, locationsPath = "server-locations.txt"):
+ """
+ Reads the locations at which the Mochitest HTTP server is available from
+ server-locations.txt.
+ """
+
+ locationFile = codecs.open(locationsPath, "r", "UTF-8")
+
+ # Perhaps more detail than necessary, but it's the easiest way to make sure
+ # we get exactly the format we want. See server-locations.txt for the exact
+ # format guaranteed here.
+ lineRe = re.compile(r"^(?P<scheme>[a-z][-a-z0-9+.]*)"
r"://"
r"(?P<host>"
r"\d+\.\d+\.\d+\.\d+"
r"|"
r"(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*"
r"[a-z](?:[-a-z0-9]*[a-z0-9])?"
r")"
r":"
r"(?P<port>\d+)"
r"(?:"
r"\s+"
r"(?P<options>\S+(?:,\S+)*)"
r")?$")
- locations = []
- lineno = 0
- seenPrimary = False
- for line in locationFile:
- lineno += 1
- if line.startswith("#") or line == "\n":
- continue
+ locations = []
+ lineno = 0
+ seenPrimary = False
+ for line in locationFile:
+ lineno += 1
+ if line.startswith("#") or line == "\n":
+ continue
- match = lineRe.match(line)
- if not match:
- raise SyntaxError(lineno)
+ match = lineRe.match(line)
+ if not match:
+ raise SyntaxError(lineno)
- options = match.group("options")
- if options:
- options = options.split(",")
- if "primary" in options:
- if seenPrimary:
- raise SyntaxError(lineno, "multiple primary locations")
- seenPrimary = True
- else:
- options = []
+ options = match.group("options")
+ if options:
+ options = options.split(",")
+ if "primary" in options:
+ if seenPrimary:
+ raise SyntaxError(lineno, "multiple primary locations")
+ seenPrimary = True
+ else:
+ options = []
- locations.append(Location(match.group("scheme"), match.group("host"),
- match.group("port"), options))
+ locations.append(Location(match.group("scheme"), match.group("host"),
+ match.group("port"), options))
- if not seenPrimary:
- raise SyntaxError(lineno + 1, "missing primary location")
+ if not seenPrimary:
+ raise SyntaxError(lineno + 1, "missing primary location")
- return locations
+ return locations
-def initializeProfile(profileDir):
- "Sets up the standard testing profile."
+ def initializeProfile(self, profileDir, extraPrefs = []):
+ "Sets up the standard testing profile."
- # Start with a clean slate.
- shutil.rmtree(profileDir, True)
- os.mkdir(profileDir)
+ # Start with a clean slate.
+ shutil.rmtree(profileDir, True)
+ os.mkdir(profileDir)
- prefs = []
+ prefs = []
- part = """\
+ part = """\
user_pref("browser.dom.window.dump.enabled", true);
user_pref("dom.allow_scripts_to_close_windows", true);
user_pref("dom.disable_open_during_load", false);
user_pref("dom.max_script_run_time", 0); // no slow script dialogs
user_pref("dom.max_chrome_script_run_time", 0);
+user_pref("dom.popup_maximum", -1);
user_pref("signed.applets.codebase_principal_support", true);
user_pref("security.warn_submit_insecure", false);
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("shell.checkDefaultClient", false);
user_pref("browser.warnOnQuit", false);
user_pref("accessibility.typeaheadfind.autostart", false);
user_pref("javascript.options.showInConsole", true);
user_pref("layout.debug.enable_data_xbl", true);
user_pref("browser.EULA.override", true);
user_pref("javascript.options.jit.content", true);
user_pref("gfx.color_management.force_srgb", true);
user_pref("network.manage-offline-status", false);
user_pref("test.mousescroll", true);
user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs
user_pref("network.http.prompt-temp-redirect", false);
-user_pref("svg.smil.enabled", true); // Needed for SMIL mochitests until bug 482402 lands
user_pref("media.cache_size", 100);
user_pref("security.warn_viewing_mixed", false);
user_pref("geo.wifi.uri", "http://localhost:8888/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs");
user_pref("geo.wifi.testing", true);
user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
@@ -266,40 +283,40 @@ user_pref("camino.warn_when_closing", fa
user_pref("urlclassifier.updateinterval", 172800);
// Point the url-classifier to the local testing server for fast failures
user_pref("browser.safebrowsing.provider.0.gethashURL", "http://localhost:8888/safebrowsing-dummy/gethash");
user_pref("browser.safebrowsing.provider.0.keyURL", "http://localhost:8888/safebrowsing-dummy/newkey");
user_pref("browser.safebrowsing.provider.0.lookupURL", "http://localhost:8888/safebrowsing-dummy/lookup");
user_pref("browser.safebrowsing.provider.0.updateURL", "http://localhost:8888/safebrowsing-dummy/update");
"""
- prefs.append(part)
+ prefs.append(part)
- locations = readLocations()
+ locations = self.readLocations()
- # Grant God-power to all the privileged servers on which tests run.
- privileged = filter(lambda loc: "privileged" in loc.options, locations)
- for (i, l) in itertools.izip(itertools.count(1), privileged):
- part = """
+ # Grant God-power to all the privileged servers on which tests run.
+ privileged = filter(lambda loc: "privileged" in loc.options, locations)
+ for (i, l) in itertools.izip(itertools.count(1), privileged):
+ part = """
user_pref("capability.principal.codebase.p%(i)d.granted",
"UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite \
UniversalPreferencesRead UniversalPreferencesWrite \
UniversalFileRead");
user_pref("capability.principal.codebase.p%(i)d.id", "%(origin)s");
user_pref("capability.principal.codebase.p%(i)d.subjectName", "");
""" % { "i": i,
"origin": (l.scheme + "://" + l.host + ":" + l.port) }
- prefs.append(part)
+ prefs.append(part)
- # We need to proxy every server but the primary one.
- origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port)
- for l in filter(lambda l: "primary" not in l.options, locations)]
- origins = ", ".join(origins)
+ # We need to proxy every server but the primary one.
+ origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port)
+ for l in filter(lambda l: "primary" not in l.options, locations)]
+ origins = ", ".join(origins)
- pacURL = """data:text/plain,
+ pacURL = """data:text/plain,
function FindProxyForURL(url, host)
{
var origins = [%(origins)s];
var regex = new RegExp('^([a-z][-a-z0-9+.]*)' +
'://' +
'(?:[^/@]*@)?' +
'(.*?)' +
'(?::(\\\\\\\\d+))?/');
@@ -318,213 +335,416 @@ function FindProxyForURL(url, host)
if (origins.indexOf(origin) < 0)
return 'DIRECT';
if (isHttp)
return 'PROXY 127.0.0.1:8888';
if (isHttps)
return 'PROXY 127.0.0.1:4443';
return 'DIRECT';
}""" % { "origins": origins }
- pacURL = "".join(pacURL.splitlines())
+ pacURL = "".join(pacURL.splitlines())
- part = """
+ part = """
user_pref("network.proxy.type", 2);
user_pref("network.proxy.autoconfig_url", "%(pacURL)s");
user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless to others
""" % {"pacURL": pacURL}
- prefs.append(part)
+ prefs.append(part)
+
+ for v in extraPrefs:
+ thispref = v.split("=")
+ if len(thispref) < 2:
+ print "Error: syntax error in --setpref=" + v
+ sys.exit(1)
+ part = 'user_pref("%s", %s);\n' % (thispref[0], thispref[1])
+ prefs.append(part)
+
+ # write the preferences
+ prefsFile = open(profileDir + "/" + "user.js", "a")
+ prefsFile.write("".join(prefs))
+ prefsFile.close()
- # write the preferences
- prefsFile = open(profileDir + "/" + "user.js", "a")
- prefsFile.write("".join(prefs))
- prefsFile.close()
+ def addCommonOptions(self, parser):
+ "Adds command-line options which are common to mochitest and reftest."
+
+ parser.add_option("--setpref",
+ action = "append", type = "string",
+ default = [],
+ dest = "extraPrefs", metavar = "PREF=VALUE",
+ help = "defines an extra user preference")
-def fillCertificateDB(profileDir, certPath, utilityPath, xrePath):
- pwfilePath = os.path.join(profileDir, ".crtdbpw")
+ def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath):
+ pwfilePath = os.path.join(profileDir, ".crtdbpw")
+
+ pwfile = open(pwfilePath, "w")
+ pwfile.write("\n")
+ pwfile.close()
+
+ # Create head of the ssltunnel configuration file
+ sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg")
+ sslTunnelConfig = open(sslTunnelConfigPath, "w")
- pwfile = open(pwfilePath, "w")
- pwfile.write("\n")
- pwfile.close()
+ sslTunnelConfig.write("httpproxy:1\n")
+ sslTunnelConfig.write("certdbdir:%s\n" % certPath)
+ sslTunnelConfig.write("forward:127.0.0.1:8888\n")
+ sslTunnelConfig.write("listen:*:4443:pgo server certificate\n")
+
+ # Configure automatic certificate and bind custom certificates, client authentication
+ locations = self.readLocations()
+ locations.pop(0)
+ for loc in locations:
+ if loc.scheme == "https" and "nocert" not in loc.options:
+ customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
+ clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
+ for option in loc.options:
+ match = customCertRE.match(option)
+ if match:
+ customcert = match.group("nickname");
+ sslTunnelConfig.write("listen:%s:%s:4443:%s\n" %
+ (loc.host, loc.port, customcert))
+
+ match = clientAuthRE.match(option)
+ if match:
+ clientauth = match.group("clientauth");
+ sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" %
+ (loc.host, loc.port, clientauth))
+
+ sslTunnelConfig.close()
+
+ # Pre-create the certification database for the profile
+ env = self.environment(xrePath = xrePath)
+ certutil = os.path.join(utilityPath, "certutil" + self.BIN_SUFFIX)
+ pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX)
+
+ status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait()
+ if status != 0:
+ return status
- # Create head of the ssltunnel configuration file
- sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg")
- sslTunnelConfig = open(sslTunnelConfigPath, "w")
-
- sslTunnelConfig.write("httpproxy:1\n")
- sslTunnelConfig.write("certdbdir:%s\n" % certPath)
- sslTunnelConfig.write("forward:127.0.0.1:8888\n")
- sslTunnelConfig.write("listen:*:4443:pgo server certificate\n")
+ # Walk the cert directory and add custom CAs and client certs
+ files = os.listdir(certPath)
+ for item in files:
+ root, ext = os.path.splitext(item)
+ if ext == ".ca":
+ trustBits = "CT,,"
+ if root.endswith("-object"):
+ trustBits = "CT,,CT"
+ self.Process([certutil, "-A", "-i", os.path.join(certPath, item),
+ "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits],
+ env = env).wait()
+ if ext == ".client":
+ self.Process([pk12util, "-i", os.path.join(certPath, item), "-w",
+ pwfilePath, "-d", profileDir],
+ env = env).wait()
+
+ os.unlink(pwfilePath)
+ return 0
+
+ def environment(self, env = None, xrePath = None, crashreporter = True):
+ if xrePath == None:
+ xrePath = self.DIST_BIN
+ if env == None:
+ env = dict(os.environ)
+
+ ldLibraryPath = os.path.abspath(os.path.join(self.SCRIPT_DIR, xrePath))
+ if self.UNIXISH or self.IS_MAC:
+ envVar = "LD_LIBRARY_PATH"
+ if self.IS_MAC:
+ envVar = "DYLD_LIBRARY_PATH"
+ else: # unixish
+ env['MOZILLA_FIVE_HOME'] = xrePath
+ if envVar in env:
+ ldLibraryPath = ldLibraryPath + ":" + env[envVar]
+ env[envVar] = ldLibraryPath
+ elif self.IS_WIN32:
+ env["PATH"] = env["PATH"] + ";" + ldLibraryPath
+
+ if crashreporter:
+ env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
+ env['MOZ_CRASHREPORTER'] = '1'
+ else:
+ env['MOZ_CRASHREPORTER_DISABLE'] = '1'
+
+ env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
+ env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
+ return env
+
+ if IS_WIN32:
+ ctypes = __import__('ctypes')
+ wintypes = __import__('ctypes.wintypes')
+ time = __import__('time')
+ msvcrt = __import__('msvcrt')
+ PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
+ GetLastError = ctypes.windll.kernel32.GetLastError
- # Configure automatic certificate and bind custom certificates, client authentication
- locations = readLocations()
- locations.pop(0)
- for loc in locations:
- if loc.scheme == "https" and "nocert" not in loc.options:
- customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
- clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
- for option in loc.options:
- match = customCertRE.match(option)
- if match:
- customcert = match.group("nickname");
- sslTunnelConfig.write("listen:%s:%s:4443:%s\n" %
- (loc.host, loc.port, customcert))
+ def readWithTimeout(self, f, timeout):
+ """Try to read a line of output from the file object |f|.
+ |f| must be a pipe, like the |stdout| member of a subprocess.Popen
+ object created with stdout=PIPE. If no output
+ is received within |timeout| seconds, return a blank line.
+ Returns a tuple (line, did_timeout), where |did_timeout| is True
+ if the read timed out, and False otherwise."""
+ if timeout is None:
+ # shortcut to allow callers to pass in "None" for no timeout.
+ return (f.readline(), False)
+ x = self.msvcrt.get_osfhandle(f.fileno())
+ l = self.ctypes.c_long()
+ done = self.time.time() + timeout
+ while self.time.time() < done:
+ if self.PeekNamedPipe(x, None, 0, None, self.ctypes.byref(l), None) == 0:
+ err = self.GetLastError()
+ if err == 38 or err == 109: # ERROR_HANDLE_EOF || ERROR_BROKEN_PIPE
+ return ('', False)
+ else:
+ log.error("readWithTimeout got error: %d", err)
+ if l.value > 0:
+ # we're assuming that the output is line-buffered,
+ # which is not unreasonable
+ return (f.readline(), False)
+ self.time.sleep(0.01)
+ return ('', True)
+
+ def isPidAlive(self, pid):
+ STILL_ACTIVE = 259
+ PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
+ pHandle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid)
+ if not pHandle:
+ return False
+ pExitCode = ctypes.wintypes.DWORD()
+ self.ctypes.windll.kernel32.GetExitCodeProcess(pHandle, self.ctypes.byref(pExitCode))
+ self.ctypes.windll.kernel32.CloseHandle(pHandle)
+ if (pExitCode.value == STILL_ACTIVE):
+ return True
+ else:
+ return False
+
+ def killPid(self, pid):
+ PROCESS_TERMINATE = 0x0001
+ pHandle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, 0, pid)
+ if not pHandle:
+ return
+ success = self.ctypes.windll.kernel32.TerminateProcess(pHandle, 1)
+ self.ctypes.windll.kernel32.CloseHandle(pHandle)
+
+ else:
+ errno = __import__('errno')
+
+ def readWithTimeout(self, f, timeout):
+ """Try to read a line of output from the file object |f|. If no output
+ is received within |timeout| seconds, return a blank line.
+ Returns a tuple (line, did_timeout), where |did_timeout| is True
+ if the read timed out, and False otherwise."""
+ (r, w, e) = select.select([f], [], [], timeout)
+ if len(r) == 0:
+ return ('', True)
+ return (f.readline(), False)
+
+ def isPidAlive(self, pid):
+ try:
+ # kill(pid, 0) checks for a valid PID without actually sending a signal
+ # The method throws OSError if the PID is invalid, which we catch below.
+ os.kill(pid, 0)
+
+ # Wait on it to see if it's a zombie. This can throw OSError.ECHILD if
+ # the process terminates before we get to this point.
+ wpid, wstatus = os.waitpid(pid, os.WNOHANG)
+ if wpid == 0:
+ return True
- match = clientAuthRE.match(option)
- if match:
- clientauth = match.group("clientauth");
- sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" %
- (loc.host, loc.port, clientauth))
+ return False
+ except OSError, err:
+ # Catch the errors we might expect from os.kill/os.waitpid,
+ # and re-raise any others
+ if err.errno == self.errno.ESRCH or err.errno == self.errno.ECHILD:
+ return False
+ raise
+
+ def killPid(self, pid):
+ os.kill(pid, signal.SIGKILL)
- sslTunnelConfig.close()
+ def triggerBreakpad(self, proc, utilityPath):
+ """Attempt to kill this process in a way that triggers Breakpad crash
+ reporting, if we know how for this platform. Otherwise just .kill() it."""
+ if self.CRASHREPORTER:
+ if self.UNIXISH:
+ # ABRT will get picked up by Breakpad's signal handler
+ os.kill(proc.pid, signal.SIGABRT)
+ return
+ elif self.IS_WIN32:
+ # We should have a "crashinject" program in our utility path
+ crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
+ if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0:
+ return
+ #TODO: kill the process such that it triggers Breakpad on OS X (bug 525296)
+ self.log.info("Can't trigger Breakpad, just killing process")
+ proc.kill()
- # Pre-create the certification database for the profile
- env = environment(xrePath = xrePath)
- certutil = os.path.join(utilityPath, "certutil" + BIN_SUFFIX)
- pk12util = os.path.join(utilityPath, "pk12util" + BIN_SUFFIX)
+ def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime):
+ """ Look for timeout or crashes and return the status after the process terminates """
+ stackFixerProcess = None
+ stackFixerModule = None
+ didTimeout = False
+ if proc.stdout is None:
+ self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection")
+ else:
+ logsource = proc.stdout
+ if self.IS_DEBUG_BUILD and self.IS_LINUX:
+ # Run logsource through fix-linux-stack.pl
+ stackFixerProcess = self.Process([self.PERL, os.path.join(utilityPath, "fix-linux-stack.pl")],
+ stdin=logsource,
+ stdout=subprocess.PIPE)
+ logsource = stackFixerProcess.stdout
+
+ if self.IS_DEBUG_BUILD and self.IS_MAC:
+ # Import fix_macosx_stack.py from utilityPath
+ sys.path.insert(0, utilityPath)
+ import fix_macosx_stack as stackFixerModule
+ del sys.path[0]
- status = Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait()
- if status != 0:
+ (line, didTimeout) = self.readWithTimeout(logsource, timeout)
+ hitMaxTime = False
+ while line != "" and not didTimeout:
+ if stackFixerModule:
+ line = stackFixerModule.fixSymbols(line)
+ self.log.info(line.rstrip())
+ (line, didTimeout) = self.readWithTimeout(logsource, timeout)
+ if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
+ # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
+ hitMaxTime = True
+ self.log.info("TEST-UNEXPECTED-FAIL | automation.py | application ran for longer than allowed maximum time of %d seconds", int(maxTime))
+ self.triggerBreakpad(proc, utilityPath)
+ if didTimeout:
+ self.log.info("TEST-UNEXPECTED-FAIL | automation.py | application timed out after %d seconds with no output", int(timeout))
+ self.triggerBreakpad(proc, utilityPath)
+
+ status = proc.wait()
+ if status != 0 and not didTimeout and not hitMaxTime:
+ self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Exited with code %d during test run", status)
+ if stackFixerProcess is not None:
+ fixerStatus = stackFixerProcess.wait()
+ if fixerStatus != 0 and not didTimeout and not hitMaxTime:
+ self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus)
return status
- # Walk the cert directory and add custom CAs and client certs
- files = os.listdir(certPath)
- for item in files:
- root, ext = os.path.splitext(item)
- if ext == ".ca":
- trustBits = "CT,,"
- if root.endswith("-object"):
- trustBits = "CT,,CT"
- Process([certutil, "-A", "-i", os.path.join(certPath, item),
- "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits],
- env = env).wait()
- if ext == ".client":
- Process([pk12util, "-i", os.path.join(certPath, item), "-w",
- pwfilePath, "-d", profileDir],
- env = env).wait()
+ def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
+ """ build the application command line """
+
+ cmd = app
+ if self.IS_MAC and not self.IS_CAMINO and not cmd.endswith("-bin"):
+ cmd += "-bin"
+ cmd = os.path.abspath(cmd)
+
+ args = []
- os.unlink(pwfilePath)
- return 0
+ if debuggerInfo:
+ args.extend(debuggerInfo["args"])
+ args.append(cmd)
+ cmd = os.path.abspath(debuggerInfo["path"])
-def environment(env = None, xrePath = DIST_BIN, crashreporter = True):
- if env == None:
- env = dict(os.environ)
+ if self.IS_MAC:
+ args.append("-foreground")
+
+ if self.IS_CYGWIN:
+ profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"")
+ else:
+ profileDirectory = profileDir + "/"
- ldLibraryPath = os.path.abspath(os.path.join(SCRIPT_DIR, xrePath))
- if UNIXISH or IS_MAC:
- envVar = "LD_LIBRARY_PATH"
- if IS_MAC:
- envVar = "DYLD_LIBRARY_PATH"
- if envVar in env:
- ldLibraryPath = ldLibraryPath + ":" + env[envVar]
- env[envVar] = ldLibraryPath
- elif IS_WIN32:
- env["PATH"] = env["PATH"] + ";" + ldLibraryPath
+ args.extend(("-no-remote", "-profile", profileDirectory))
+ if testURL is not None:
+ if self.IS_CAMINO:
+ args.extend(("-url", testURL))
+ else:
+ args.append((testURL))
+ args.extend(extraArgs)
+ return cmd, args
- if crashreporter:
- env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
- env['MOZ_CRASHREPORTER'] = '1'
-
- return env
-
-
-###############
-# RUN THE APP #
-###############
-
-def runApp(testURL, env, app, profileDir, extraArgs,
- runSSLTunnel = False, utilityPath = DIST_BIN,
- xrePath = DIST_BIN, certPath = CERTS_SRC_DIR,
- debuggerInfo = None, symbolsPath = SYMBOLS_PATH):
- "Run the app, log the duration it took to execute, return the status code."
+ def checkForZombies(self, processLog):
+ """ Look for hung processes """
+ if not os.path.exists(processLog):
+ self.log.info('INFO | automation.py | PID log not found: %s', processLog)
+ else:
+ self.log.info('INFO | automation.py | Reading PID log: %s', processLog)
+ processList = []
+ pidRE = re.compile(r'launched child process (\d+)$')
+ processLogFD = open(processLog)
+ for line in processLogFD:
+ self.log.info(line.rstrip())
+ m = pidRE.search(line)
+ if m:
+ processList.append(int(m.group(1)))
+ processLogFD.close()
- if IS_TEST_BUILD and runSSLTunnel:
- # create certificate database for the profile
- certificateStatus = fillCertificateDB(profileDir, certPath, utilityPath, xrePath)
- if certificateStatus != 0:
- log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed")
- return certificateStatus
-
- # start ssltunnel to provide https:// URLs capability
- ssltunnel = os.path.join(utilityPath, "ssltunnel" + BIN_SUFFIX)
- ssltunnelProcess = Process([ssltunnel, os.path.join(profileDir, "ssltunnel.cfg")], env = environment(xrePath = xrePath))
- log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
+ for processPID in processList:
+ self.log.info("INFO | automation.py | Checking for orphan process with PID: %d", processPID)
+ if self.isPidAlive(processPID):
+ self.log.info("TEST-UNEXPECTED-FAIL | automation.py | child process %d still alive after shutdown", processPID)
+ self.killPid(processPID)
- # now run with the profile we created
- cmd = app
- if IS_MAC and not IS_CAMINO and not cmd.endswith("-bin"):
- cmd += "-bin"
- cmd = os.path.abspath(cmd)
+ def runApp(self, testURL, env, app, profileDir, extraArgs,
+ runSSLTunnel = False, utilityPath = None,
+ xrePath = None, certPath = None,
+ debuggerInfo = None, symbolsPath = None,
+ timeout = -1, maxTime = None):
+ """
+ Run the app, log the duration it took to execute, return the status code.
+ Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
+ """
- args = []
-
- if debuggerInfo:
- args.extend(debuggerInfo["args"])
- args.append(cmd)
- cmd = os.path.abspath(debuggerInfo["path"])
+ if utilityPath == None:
+ utilityPath = self.DIST_BIN
+ if xrePath == None:
+ xrePath = self.DIST_BIN
+ if certPath == None:
+ certPath = self.CERTS_SRC_DIR
+ if timeout == -1:
+ timeout = self.DEFAULT_TIMEOUT
- if IS_MAC:
- args.append("-foreground")
+ # copy env so we don't munge the caller's environment
+ env = dict(env);
+ env["NO_EM_RESTART"] = "1"
+ tmpfd, processLog = tempfile.mkstemp(suffix='pidlog')
+ os.close(tmpfd)
+ env["MOZ_PROCESS_LOG"] = processLog
+
+ if self.IS_TEST_BUILD and runSSLTunnel:
+ # create certificate database for the profile
+ certificateStatus = self.fillCertificateDB(profileDir, certPath, utilityPath, xrePath)
+ if certificateStatus != 0:
+ self.log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed")
+ return certificateStatus
- if IS_CYGWIN:
- profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"")
- else:
- profileDirectory = profileDir + "/"
+ # start ssltunnel to provide https:// URLs capability
+ ssltunnel = os.path.join(utilityPath, "ssltunnel" + self.BIN_SUFFIX)
+ ssltunnelProcess = self.Process([ssltunnel,
+ os.path.join(profileDir, "ssltunnel.cfg")],
+ env = self.environment(xrePath = xrePath))
+ self.log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
- args.extend(("-no-remote", "-profile", profileDirectory))
- if testURL is not None:
- if IS_CAMINO:
- args.extend(("-url", testURL))
+ cmd, args = self.buildCommandLine(app, debuggerInfo, profileDir, testURL, extraArgs)
+ startTime = datetime.now()
+
+ # Don't redirect stdout and stderr if an interactive debugger is attached
+ if debuggerInfo and debuggerInfo["interactive"]:
+ outputPipe = None
else:
- args.append((testURL))
- args.extend(extraArgs)
-
- startTime = datetime.now()
+ outputPipe = subprocess.PIPE
- # Don't redirect stdout and stderr if an interactive debugger is attached
- if debuggerInfo and debuggerInfo["interactive"]:
- outputPipe = None
- else:
- outputPipe = subprocess.PIPE
-
- proc = Process([cmd] + args,
- env = environment(env, xrePath = xrePath,
+ proc = self.Process([cmd] + args,
+ env = self.environment(env, xrePath = xrePath,
crashreporter = not debuggerInfo),
stdout = outputPipe,
stderr = subprocess.STDOUT)
- log.info("INFO | automation.py | Application pid: %d", proc.pid)
+ self.log.info("INFO | automation.py | Application pid: %d", proc.pid)
- stackFixerProcess = None
- if outputPipe is None:
- log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection")
- else:
- logsource = proc.stdout
- if IS_DEBUG_BUILD:
- stackFixerCommand = None
- if IS_MAC:
- stackFixerCommand = "fix-macosx-stack.pl"
- elif IS_LINUX:
- stackFixerCommand = "fix-linux-stack.pl"
- if stackFixerCommand is not None:
- stackFixerProcess = Process([PERL, os.path.join(utilityPath, stackFixerCommand)], stdin=logsource, stdout=subprocess.PIPE)
- logsource = stackFixerProcess.stdout
+ status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime)
+ self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
- line = logsource.readline()
- while line != "":
- log.info(line.rstrip())
- line = logsource.readline()
+ # Do a final check for zombie child processes.
+ self.checkForZombies(processLog)
+ self.automationutils.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath)
- status = proc.wait()
- if status != 0:
- log.info("TEST-UNEXPECTED-FAIL | automation.py | Exited with code %d during test run", status)
- if stackFixerProcess is not None:
- status = stackFixerProcess.wait()
- if status != 0:
- log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", status)
- log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
+ if os.path.exists(processLog):
+ os.unlink(processLog)
- if checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath):
- status = -1
+ if self.IS_TEST_BUILD and runSSLTunnel:
+ ssltunnelProcess.kill()
- if IS_TEST_BUILD and runSSLTunnel:
- ssltunnelProcess.kill()
-
- return status
+ return status
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -31,74 +31,162 @@
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK ***** */
-import glob, logging, os, subprocess, sys
+import glob, logging, os, shutil, subprocess, sys
import re
__all__ = [
"addCommonOptions",
"checkForCrashes",
"dumpLeakLog",
"processLeakLog",
+ "getDebuggerInfo",
+ "DEBUGGER_INFO",
]
+# Map of debugging programs to information about them, like default arguments
+# and whether or not they are interactive.
+DEBUGGER_INFO = {
+ # gdb requires that you supply the '--args' flag in order to pass arguments
+ # after the executable name to the executable.
+ "gdb": {
+ "interactive": True,
+ "args": "-q --args"
+ },
+
+ # valgrind doesn't explain much about leaks unless you set the
+ # '--leak-check=full' flag.
+ "valgrind": {
+ "interactive": False,
+ "args": "--leak-check=full"
+ }
+}
+
log = logging.getLogger()
def addCommonOptions(parser, defaults={}):
parser.add_option("--xre-path",
action = "store", type = "string", dest = "xrePath",
# individual scripts will set a sane default
default = None,
help = "absolute path to directory containing XRE (probably xulrunner)")
if 'SYMBOLS_PATH' not in defaults:
defaults['SYMBOLS_PATH'] = None
parser.add_option("--symbols-path",
action = "store", type = "string", dest = "symbolsPath",
default = defaults['SYMBOLS_PATH'],
help = "absolute path to directory containing breakpad symbols")
+ parser.add_option("--debugger",
+ action = "store", dest = "debugger",
+ help = "use the given debugger to launch the application")
+ parser.add_option("--debugger-args",
+ action = "store", dest = "debuggerArgs",
+ help = "pass the given args to the debugger _before_ "
+ "the application on the command line")
+ parser.add_option("--debugger-interactive",
+ action = "store_true", dest = "debuggerInteractive",
+ help = "prevents the test harness from redirecting "
+ "stdout and stderr for interactive debuggers")
def checkForCrashes(dumpDir, symbolsPath, testName=None):
stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None)
# try to get the caller's filename if no test name is given
if testName is None:
try:
testName = os.path.basename(sys._getframe(1).f_code.co_filename)
except:
testName = "unknown"
foundCrash = False
dumps = glob.glob(os.path.join(dumpDir, '*.dmp'))
for d in dumps:
- log.info("TEST-UNEXPECTED-FAIL | %s | application crashed (minidump found)", testName)
+ log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName)
if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath):
nullfd = open(os.devnull, 'w')
# eat minidump_stackwalk errors
subprocess.call([stackwalkPath, d, symbolsPath], stderr=nullfd)
nullfd.close()
else:
if not symbolsPath:
print "No symbols path given, can't process dump."
if not stackwalkPath:
print "MINIDUMP_STACKWALK not set, can't process dump."
else:
if not os.path.exists(stackwalkPath):
print "MINIDUMP_STACKWALK binary not found: %s" % stackwalkPath
- os.remove(d)
+ dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None)
+ if dumpSavePath:
+ shutil.move(d, dumpSavePath)
+ print "Saved dump as %s" % os.path.join(dumpSavePath,
+ os.path.basename(d))
+ else:
+ os.remove(d)
extra = os.path.splitext(d)[0] + ".extra"
if os.path.exists(extra):
os.remove(extra)
foundCrash = True
return foundCrash
+
+def getFullPath(directory, path):
+ "Get an absolute path relative to 'directory'."
+ return os.path.normpath(os.path.join(directory, os.path.expanduser(path)))
+
+def searchPath(directory, path):
+ "Go one step beyond getFullPath and try the various folders in PATH"
+ # Try looking in the current working directory first.
+ newpath = getFullPath(directory, path)
+ if os.path.isfile(newpath):
+ return newpath
+
+ # At this point we have to fail if a directory was given (to prevent cases
+ # like './gdb' from matching '/usr/bin/./gdb').
+ if not os.path.dirname(path):
+ for dir in os.environ['PATH'].split(os.pathsep):
+ newpath = os.path.join(dir, path)
+ if os.path.isfile(newpath):
+ return newpath
+ return None
+
+def getDebuggerInfo(directory, debugger, debuggerArgs, debuggerInteractive = False):
+
+ debuggerInfo = None
+
+ if debugger:
+ debuggerPath = searchPath(directory, debugger)
+ if not debuggerPath:
+ print "Error: Path %s doesn't exist." % debugger
+ sys.exit(1)
+
+ debuggerName = os.path.basename(debuggerPath).lower()
+
+ def getDebuggerInfo(type, default):
+ if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]:
+ return DEBUGGER_INFO[debuggerName][type]
+ return default
+
+ debuggerInfo = {
+ "path": debuggerPath,
+ "interactive" : getDebuggerInfo("interactive", False),
+ "args": getDebuggerInfo("args", "").split()
+ }
+
+ if debuggerArgs:
+ debuggerInfo["args"] = debuggerArgs.split()
+ if debuggerInteractive:
+ debuggerInfo["interactive"] = debuggerInteractive
+
+ return debuggerInfo
+
def dumpLeakLog(leakLogFile, filter = False):
"""Process the leak log, without parsing it.
Use this function if you want the raw log only.
Use it preferably with the |XPCOM_MEM_LEAK_LOG| environment variable.
"""
@@ -113,87 +201,125 @@ def dumpLeakLog(leakLogFile, filter = Fa
# Only |XPCOM_MEM_LEAK_LOG| reports can be actually filtered out.
# Only check whether an actual leak was reported.
if filter and not "0 TOTAL " in leakReport:
return
# Simply copy the log.
log.info(leakReport.rstrip("\n"))
-def processLeakLog(leakLogFile, leakThreshold = 0):
- """Process the leak log, parsing it.
-
- Use this function if you want an additional PASS/FAIL summary.
- It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.
+def processSingleLeakFile(leakLogFileName, PID, processType, leakThreshold):
+ """Process a single leak log, corresponding to the specified
+ process PID and type.
"""
- if not os.path.exists(leakLogFile):
- log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!")
- return
-
# Per-Inst Leaked Total Rem ...
# 0 TOTAL 17 192 419115886 2 ...
# 833 nsTimerImpl 60 120 24726 2 ...
lineRe = re.compile(r"^\s*\d+\s+(?P<name>\S+)\s+"
r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+"
r"-?\d+\s+(?P<numLeaked>-?\d+)")
- leaks = open(leakLogFile, "r")
+ processString = ""
+ if PID and processType:
+ processString = "| %s process %s " % (processType, PID)
+ leaks = open(leakLogFileName, "r")
for line in leaks:
matches = lineRe.match(line)
if (matches and
int(matches.group("numLeaked")) == 0 and
matches.group("name") != "TOTAL"):
continue
log.info(line.rstrip())
leaks.close()
- leaks = open(leakLogFile, "r")
+ leaks = open(leakLogFileName, "r")
seenTotal = False
+ crashedOnPurpose = False
prefix = "TEST-PASS"
for line in leaks:
+ if line.find("purposefully crash") > -1:
+ crashedOnPurpose = True
matches = lineRe.match(line)
if not matches:
continue
name = matches.group("name")
size = int(matches.group("size"))
bytesLeaked = int(matches.group("bytesLeaked"))
numLeaked = int(matches.group("numLeaked"))
if size < 0 or bytesLeaked < 0 or numLeaked < 0:
- log.info("TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | negative leaks caught!")
+ log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | negative leaks caught!" %
+ processString)
if name == "TOTAL":
seenTotal = True
elif name == "TOTAL":
seenTotal = True
# Check for leaks.
if bytesLeaked < 0 or bytesLeaked > leakThreshold:
prefix = "TEST-UNEXPECTED-FAIL"
- leakLog = "TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | leaked" \
- " %d bytes during test execution" % bytesLeaked
+ leakLog = "TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | leaked" \
+ " %d bytes during test execution" % (processString, bytesLeaked)
elif bytesLeaked > 0:
- leakLog = "TEST-PASS | automationutils.processLeakLog() | WARNING leaked" \
- " %d bytes during test execution" % bytesLeaked
+ leakLog = "TEST-PASS %s| automationutils.processLeakLog() | WARNING leaked" \
+ " %d bytes during test execution" % (processString, bytesLeaked)
else:
- leakLog = "TEST-PASS | automationutils.processLeakLog() | no leaks detected!"
+ leakLog = "TEST-PASS %s| automationutils.processLeakLog() | no leaks detected!" \
+ % processString
# Remind the threshold if it is not 0, which is the default/goal.
if leakThreshold != 0:
leakLog += " (threshold set at %d bytes)" % leakThreshold
# Log the information.
log.info(leakLog)
else:
if numLeaked != 0:
if numLeaked > 1:
instance = "instances"
rest = " each (%s bytes total)" % matches.group("bytesLeaked")
else:
instance = "instance"
rest = ""
- log.info("%(prefix)s | automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s "
+ log.info("%(prefix)s %(process)s| automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s "
"with size %(size)s bytes%(rest)s" %
{ "prefix": prefix,
+ "process": processString,
"numLeaked": numLeaked,
"instance": instance,
"name": name,
"size": matches.group("size"),
"rest": rest })
if not seenTotal:
- log.info("TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | missing output line for total leaks!")
+ if crashedOnPurpose:
+ log.info("INFO | automationutils.processLeakLog() | process %s was " \
+ "deliberately crashed and thus has no leak log" % PID)
+ else:
+ log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | missing output line for total leaks!" %
+ processString)
leaks.close()
+
+
+def processLeakLog(leakLogFile, leakThreshold = 0):
+ """Process the leak log, including separate leak logs created
+ by child processes.
+
+ Use this function if you want an additional PASS/FAIL summary.
+ It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.
+ """
+
+ if not os.path.exists(leakLogFile):
+ log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!")
+ return
+
+ (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile)
+ pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*)$")
+ if leakFileBase[-4:] == ".log":
+ leakFileBase = leakFileBase[:-4]
+ pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*).log$")
+
+ for fileName in os.listdir(leakLogFileDir):
+ if fileName.find(leakFileBase) != -1:
+ thisFile = os.path.join(leakLogFileDir, fileName)
+ processPID = 0
+ processType = None
+ m = pidRegExp.search(fileName)
+ if m:
+ processType = m.group(1)
+ processPID = m.group(2)
+ processSingleLeakFile(thisFile, processPID, processType, leakThreshold)
--- a/build/leaktest.py.in
+++ b/build/leaktest.py.in
@@ -41,49 +41,46 @@
import SimpleHTTPServer
import SocketServer
import threading
import os
import sys
import logging
from getopt import getopt
-import automation
+from automation import Automation
PORT = 8888
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
PROFILE_DIRECTORY = os.path.abspath(os.path.join(SCRIPT_DIR, "./leakprofile"))
-DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN)
os.chdir(SCRIPT_DIR)
class EasyServer(SocketServer.TCPServer):
allow_reuse_address = True
if __name__ == '__main__':
+ automation = Automation()
+ DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN)
opts, extraArgs = getopt(sys.argv[1:], 'l:')
if len(opts) > 0:
try:
automation.log.addHandler(logging.FileHandler(opts[0][1], "w"))
except:
automation.log.info("Unable to open logfile " + opts[0][1] + \
"ONLY logging to stdout.")
httpd = EasyServer(("", PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
t = threading.Thread(target=httpd.serve_forever)
t.setDaemon(True)
t.start()
automation.initializeProfile(PROFILE_DIRECTORY)
- browserEnv = dict(os.environ)
+ browserEnv = automation.environment()
- browserEnv["NO_EM_RESTART"] = "1"
if not "XPCOM_DEBUG_BREAK" in browserEnv:
browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
- if automation.UNIXISH:
- browserEnv["LD_LIBRARY_PATH"] = os.path.join(SCRIPT_DIR, DIST_BIN)
- browserEnv["MOZILLA_FIVE_HOME"] = os.path.join(SCRIPT_DIR, DIST_BIN)
- browserEnv["GNOME_DISABLE_CRASH_DIALOG"] = "1"
-
url = "http://localhost:%d/bloatcycle.html" % PORT
appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP)
status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY,
- extraArgs)
+ # leaktest builds are slow, give up and
+ # don't use a timeout.
+ extraArgs, timeout=None)
sys.exit(status)
--- a/build/pgo/genpgocert.py.in
+++ b/build/pgo/genpgocert.py.in
@@ -31,27 +31,29 @@
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
-import automation
+from automation import Automation
import os
import re
import shutil
import sys
#expand DIST_BIN = __XPC_BIN_PATH__
#expand BIN_SUFFIX = __BIN_SUFFIX__
#expand PROFILE_DIR = __PROFILE_DIR__
#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__
+automation = Automation()
+
dbFiles = [
re.compile("^cert[0-9]+\.db$"),
re.compile("^key[0-9]+\.db$"),
re.compile("^secmod\.db$")
]
def unlinkDbFiles(path):
for root, dirs, files in os.walk(path):
--- a/build/pgo/profileserver.py.in
+++ b/build/pgo/profileserver.py.in
@@ -41,39 +41,36 @@
import SimpleHTTPServer
import SocketServer
import socket
import threading
import os
import sys
import shutil
from datetime import datetime
-import automation
+from automation import Automation
PORT = 8888
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
PROFILE_DIRECTORY = os.path.abspath(os.path.join(SCRIPT_DIR, "./pgoprofile"))
os.chdir(SCRIPT_DIR)
class EasyServer(SocketServer.TCPServer):
allow_reuse_address = True
if __name__ == '__main__':
+ automation = Automation()
httpd = EasyServer(("", PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
t = threading.Thread(target=httpd.serve_forever)
t.setDaemon(True) # don't hang on exit
t.start()
automation.initializeProfile(PROFILE_DIRECTORY)
- browserEnv = dict(os.environ)
-
- # These variables are necessary for correct application startup; change
- # via the commandline at your own risk.
- browserEnv["NO_EM_RESTART"] = "1"
+ browserEnv = automation.environment()
browserEnv["XPCOM_DEBUG_BREAK"] = "warn"
- if automation.UNIXISH:
- browserEnv["LD_LIBRARY_PATH"] = os.path.join(SCRIPT_DIR, automation.DIST_BIN)
- browserEnv["MOZILLA_FIVE_HOME"] = os.path.join(SCRIPT_DIR, automation.DIST_BIN)
url = "http://localhost:%d/index.html" % PORT
appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP)
- status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, {})
+ status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, {},
+ # the profiling HTML doesn't output anything,
+ # so let's just run this without a timeout
+ timeout = None)
sys.exit(status)
--- a/build/win32/Makefile.in
+++ b/build/win32/Makefile.in
@@ -37,16 +37,25 @@
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
+ifdef ENABLE_TESTS
+DIRS += crashinjectdll
+
+PROGRAM = crashinject$(BIN_SUFFIX)
+USE_STATIC_LIBS = 1
+CPPSRCS = crashinject.cpp
+
+endif
+
include $(topsrcdir)/config/rules.mk
ifdef WIN32_REDIST_DIR
ifeq (1400,$(_MSC_VER))
REDIST_FILES = \
Microsoft.VC80.CRT.manifest \
msvcm80.dll \
@@ -59,18 +68,23 @@ ifeq (1500,$(_MSC_VER))
REDIST_FILES = \
Microsoft.VC90.CRT.manifest \
msvcm90.dll \
msvcp90.dll \
msvcr90.dll \
$(NULL)
endif
+ifeq (1600,$(_MSC_VER))
+REDIST_FILES = \
+ msvcp100.dll \
+ msvcr100.dll \
+ $(NULL)
+endif
+
endif
ifdef REDIST_FILES
libs::
mkdir -p $(FINAL_TARGET)
- for file in $(REDIST_FILES) ; do \
- install --preserve-timestamps "$(WIN32_REDIST_DIR)"/$$file $(FINAL_TARGET) ; \
- done
+ install --preserve-timestamps $(foreach f,$(REDIST_FILES),"$(WIN32_REDIST_DIR)"/$(f)) $(FINAL_TARGET)
endif
new file mode 100644
--- /dev/null
+++ b/build/win32/crashinject.cpp
@@ -0,0 +1,128 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Crash Injection Utility
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Ted Mielczarek <ted.mielczarek@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Given a PID, this program attempts to inject a DLL into the process
+ * with that PID. The DLL it attempts to inject, "crashinjectdll.dll",
+ * must exist alongside this exe. The DLL will then crash the process.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: crashinject <PID>\n");
+ return 1;
+ }
+
+ int pid = atoi(argv[1]);
+ if (pid <= 0) {
+ fprintf(stderr, "Usage: crashinject <PID>\n");
+ return 1;
+ }
+
+ // find our DLL to inject
+ wchar_t filename[_MAX_PATH];
+ if (GetModuleFileNameW(NULL, filename, sizeof(filename) / sizeof(wchar_t)) == 0)
+ return 1;
+
+ wchar_t* slash = wcsrchr(filename, L'\\');
+ if (slash == NULL)
+ return 1;
+
+ slash++;
+ wcscpy(slash, L"crashinjectdll.dll");
+
+ // now find our target process
+ HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD,
+ FALSE,
+ pid);
+ if (targetProc == NULL) {
+ fprintf(stderr, "Error %d opening target process\n", GetLastError());
+ return 1;
+ }
+
+ /*
+ * This is sort of insane, but we're implementing a technique described here:
+ * http://www.codeproject.com/KB/threads/winspy.aspx#section_2
+ *
+ * The gist is to use CreateRemoteThread to create a thread in the other
+ * process, but cheat and make the thread function kernel32!LoadLibrary,
+ * so that the only remote data we have to pass to the other process
+ * is the path to the library we want to load. The library we're loading
+ * will then do its dirty work inside the other process.
+ */
+ HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
+ // allocate some memory to hold the path in the remote process
+ void* pLibRemote = VirtualAllocEx(targetProc, NULL, sizeof(filename),
+ MEM_COMMIT, PAGE_READWRITE);
+ if (pLibRemote == NULL) {
+ fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError());
+ CloseHandle(targetProc);
+ return 1;
+ }
+
+ if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename,
+ sizeof(filename), NULL)) {
+ fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError());
+ VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE);
+ CloseHandle(targetProc);
+ return 1;
+ }
+ // Now create a thread in the target process that will load our DLL
+ HANDLE hThread = CreateRemoteThread(
+ targetProc, NULL, 0,
+ (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32,
+ "LoadLibraryW"),
+ pLibRemote, 0, NULL);
+ if (hThread == NULL) {
+ fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError());
+ VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE);
+ CloseHandle(targetProc);
+ return 1;
+ }
+ WaitForSingleObject(hThread, INFINITE);
+ // Cleanup, not that it's going to matter at this point
+ CloseHandle(hThread);
+ VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE);
+ CloseHandle(targetProc);
+
+ return 0;
+}
new file mode 100644
--- /dev/null
+++ b/build/win32/crashinjectdll/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla core build scripts.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation
+#
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
+#
+# Contributor(s):
+# Ted Mielczarek <ted.mielczarek@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME = crashinjectdll
+DEFFILE = $(srcdir)/crashinjectdll.def
+FORCE_SHARED_LIB = 1
+USE_STATIC_LIBS = 1
+
+CPPSRCS = crashinjectdll.cpp
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/build/win32/crashinjectdll/crashinjectdll.cpp
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <windows.h>
+
+// make sure we only ever spawn one thread
+DWORD tid = -1;
+
+DWORD WINAPI CrashingThread(
+ LPVOID lpParameter
+)
+{
+ // not a very friendly DLL
+ volatile int* x = (int *)0x0;
+ *x = 1;
+ return 0;
+}
+
+BOOL WINAPI DllMain(
+ HANDLE hinstDLL,
+ DWORD dwReason,
+ LPVOID lpvReserved
+)
+{
+ if (tid == -1)
+ // we have to crash on another thread because LoadLibrary() will
+ // catch memory access errors and return failure to the calling process
+ CreateThread(
+ NULL, // default security attributes
+ 0, // use default stack size
+ CrashingThread , // thread function name
+ NULL, // argument to thread function
+ 0, // use default creation flags
+ &tid); // returns the thread identifier
+ return TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/build/win32/crashinjectdll/crashinjectdll.def
@@ -0,0 +1,3 @@
+LIBRARY crashinjectdll
+EXPORTS
+ DllMain
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -101,16 +101,18 @@ MOZ_DEBUG_ENABLE_DEFS = @MOZ_DEBUG_ENAB
MOZ_DEBUG_DISABLE_DEFS = @MOZ_DEBUG_DISABLE_DEFS@
MOZ_DEBUG_FLAGS = @MOZ_DEBUG_FLAGS@
MOZ_DEBUG_LDFLAGS=@MOZ_DEBUG_LDFLAGS@
MOZ_DBGRINFO_MODULES = @MOZ_DBGRINFO_MODULES@
MOZ_EXTENSIONS = @MOZ_EXTENSIONS@
MOZ_IMG_DECODERS= @MOZ_IMG_DECODERS@
MOZ_IMG_ENCODERS= @MOZ_IMG_ENCODERS@
MOZ_JSDEBUGGER = @MOZ_JSDEBUGGER@
+MOZ_IPC = @MOZ_IPC@
+MOZ_IPDL_TESTS = @MOZ_IPDL_TESTS@
MOZ_PERF_METRICS = @MOZ_PERF_METRICS@
MOZ_LEAKY = @MOZ_LEAKY@
MOZ_MEMORY = @MOZ_MEMORY@
MOZ_JPROF = @MOZ_JPROF@
MOZ_SHARK = @MOZ_SHARK@
MOZ_CALLGRIND = @MOZ_CALLGRIND@
MOZ_VTUNE = @MOZ_VTUNE@
MOZ_TRACEVIS = @MOZ_TRACEVIS@
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -968,29 +968,34 @@ alltags:
rm -f TAGS
find $(topsrcdir) -name dist -prune -o \( -name '*.[hc]' -o -name '*.cp' -o -name '*.cpp' -o -name '*.idl' \) -print | $(TAG_PROGRAM)
#
# PROGRAM = Foo
# creates OBJS, links with LIBS to create Foo
#
$(PROGRAM): $(PROGOBJS) $(LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS)
+ @rm -f $@.manifest
ifeq (WINCE,$(OS_ARCH))
$(LD) -NOLOGO -OUT:$@ $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
else
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
$(LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
ifdef MSMANIFEST_TOOL
@if test -f $@.manifest; then \
if test -f "$(srcdir)/$@.manifest"; then \
+ echo "Embedding manifest from $(srcdir)/$@.manifest and $@.manifest"; \
mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" $@.manifest -OUTPUTRESOURCE:$@\;1; \
else \
+ echo "Embedding manifest from $@.manifest"; \
mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
fi; \
- rm -f $@.manifest; \
+ elif test -f "$(srcdir)/$@.manifest"; then \
+ echo "Embedding manifest from $(srcdir)/$@.manifest"; \
+ mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" -OUTPUTRESOURCE:$@\;1; \
fi
endif # MSVC with manifest tool
else
ifeq ($(CPP_PROG_LINK),1)
$(CCC) -o $@ $(CXXFLAGS) $(WRAP_MALLOC_CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(SOLARIS_JEMALLOC_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(WRAP_MALLOC_LIB) $(PROFILER_LIBS) $(EXE_DEF_FILE)
else # ! CPP_PROG_LINK
$(CC) -o $@ $(CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(SOLARIS_JEMALLOC_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE)
endif # CPP_PROG_LINK
--- a/config/static-checking-config.mk
+++ b/config/static-checking-config.mk
@@ -1,15 +1,17 @@
# The entire tree should be subject to static analysis using the XPCOM
# script. Additional scripts may be added by specific subdirectories.
DEHYDRA_SCRIPT = $(topsrcdir)/config/static-checking.js
DEHYDRA_MODULES = \
$(topsrcdir)/xpcom/analysis/final.js \
+ $(topsrcdir)/xpcom/analysis/override.js \
+ $(topsrcdir)/xpcom/analysis/must-override.js \
$(NULL)
TREEHYDRA_MODULES = \
$(topsrcdir)/xpcom/analysis/outparams.js \
$(topsrcdir)/xpcom/analysis/stack.js \
$(topsrcdir)/xpcom/analysis/flow.js \
$(topsrcdir)/js/src/jsstack.js \
$(topsrcdir)/layout/generic/frame-verify.js \
--- a/config/static-checking.js
+++ b/config/static-checking.js
@@ -52,16 +52,41 @@ function hasAttribute(c, attrname)
for each (attr in c.attributes)
if (attr.name == 'user' && attr.value[0] == attrname)
return true;
return false;
}
+// This is useful for detecting method overrides
+function signaturesMatch(m1, m2)
+{
+ if (m1.shortName != m2.shortName)
+ return false;
+
+ if (m1.isVirtual != m2.isVirtual)
+ return false;
+
+ if (m1.isStatic != m2.isStatic)
+ return false;
+
+ let p1 = m1.type.parameters;
+ let p2 = m2.type.parameters;
+
+ if (p1.length != p2.length)
+ return false;
+
+ for (let i = 0; i < p1.length; ++i)
+ if (p1[i] !== p2[i])
+ return false;
+
+ return true;
+}
+
const forward_functions = [
'process_type',
'process_tree_type',
'process_decl',
'process_tree_decl',
'process_function',
'process_tree',
'process_cp_pre_genericize',
--- a/config/system-headers
+++ b/config/system-headers
@@ -71,16 +71,17 @@ Button.h
byteswap.h
#if MOZ_ENABLE_LIBXUL!=1
#define WRAP_CAIRO_HEADERS
#endif
#if MOZ_TREE_CAIRO!=1
#define WRAP_CAIRO_HEADERS
#endif
#ifdef WRAP_CAIRO_HEADERS
+pixman.h
cairo.h
cairo-atsui.h
cairo-beos.h
cairo-ft.h
cairo-glitz.h
cairo-os2.h
cairo-pdf.h
cairo-ps.h
--- a/configure.in
+++ b/configure.in
@@ -456,21 +456,17 @@ fi
dnl Special win32 checks
dnl ========================================================
case "$target" in
*-wince)
WINVER=500
;;
*)
- if test -n "$GNU_CC"; then
- WINVER=501
- else
- WINVER=500
- fi
+ WINVER=502
;;
esac
dnl Target the Windows 7 SDK by default
WINSDK_TARGETVER=601
MOZ_ARG_WITH_STRING(windows-version,
[ --with-windows-version=WINSDK_TARGETVER
@@ -1125,16 +1121,19 @@ HOST_OS_ARCH=`echo $host_os | sed -e 's|
# OS_RELEASE
# OS_MINOR_RELEASE
#
case "$HOST_OS_ARCH" in
cygwin*|mingw*|mks*|msvc*)
HOST_OS_ARCH=WINNT
;;
+darwin*)
+ HOST_OS_ARCH=Darwin
+ ;;
linux*)
HOST_OS_ARCH=Linux
;;
solaris*)
HOST_OS_ARCH=SunOS
SOLARIS_SUNPRO_CC=
SOLARIS_SUNPRO_CXX=
if test -z "$GNU_CC"; then
@@ -4174,17 +4173,17 @@ dnl are defined in build/autoconf/altopt
dnl ========================================================
dnl =
dnl = Check for external package dependencies
dnl =
dnl ========================================================
MOZ_ARG_HEADER(External Packages)
-MOZ_ENABLE_LIBXUL=
+MOZ_ENABLE_LIBXUL=1
MOZ_ARG_WITH_STRING(libxul-sdk,
[ --with-libxul-sdk=PFX Use the libXUL SDK at <PFX>],
LIBXUL_SDK_DIR=$withval)
if test "$LIBXUL_SDK_DIR" = "yes"; then
AC_MSG_ERROR([--with-libxul-sdk must specify a path])
elif test -n "$LIBXUL_SDK_DIR" -a "$LIBXUL_SDK_DIR" != "no"; then
@@ -4503,16 +4502,17 @@ MOZ_ACTIVEX_SCRIPTING_SUPPORT=
MOZ_BRANDING_DIRECTORY=
MOZ_OFFICIAL_BRANDING=
MOZ_DBGRINFO_MODULES=
MOZ_ENABLE_CANVAS=1
MOZ_ENABLE_CANVAS3D=
MOZ_FEEDS=1
MOZ_IMG_DECODERS_DEFAULT="png gif jpeg bmp icon"
MOZ_IMG_ENCODERS_DEFAULT="png jpeg"
+MOZ_IPC=1
MOZ_JAVAXPCOM=
MOZ_JSDEBUGGER=1
MOZ_JSLOADER=1
MOZ_MATHML=1
MOZ_MORK=1
MOZ_MORKREADER=
MOZ_AUTH_EXTENSION=1
MOZ_NO_ACTIVEX_SUPPORT=1
@@ -5339,16 +5339,59 @@ dnl = JS Debugger XPCOM component (js/js
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(jsd,
[ --disable-jsd Disable JavaScript debug library],
MOZ_JSDEBUGGER=,
MOZ_JSDEBUGGER=1)
dnl ========================================================
+dnl = Disable IPC support for tabs and plugins
+dnl ========================================================
+case "${target}" in
+*-apple-darwin*)
+ MOZ_IPC=
+ ;;
+*-wince*)
+ MOZ_IPC=
+ ;;
+esac
+
+MOZ_ARG_DISABLE_BOOL(ipc,
+[ --disable-ipc Disable IPC supports for tabs and plugins],
+ MOZ_IPC=,
+ MOZ_IPC=1)
+
+if test -n "$MOZ_IPC"; then
+ AC_DEFINE(MOZ_IPC)
+fi
+
+AC_SUBST(MOZ_IPC)
+
+dnl ========================================================
+dnl = Enable IPDL's "expensive" unit tests
+dnl ========================================================
+MOZ_IPDL_TESTS=
+
+MOZ_ARG_ENABLE_BOOL(ipdl-tests,
+[ --enable-ipdl-tests Enable expensive IPDL tests],
+ MOZ_IPDL_TESTS=1,
+ MOZ_IPDL_TESTS=)
+
+if test -z "$MOZ_IPC" -a -n "$MOZ_IPDL_TESTS"; then
+ AC_MSG_ERROR([--enable-ipdl-tests requires --enable-ipc])
+fi
+
+if test -n "$MOZ_IPDL_TESTS"; then
+ AC_DEFINE(MOZ_IPDL_TESTS)
+fi
+
+AC_SUBST(MOZ_IPDL_TESTS)
+
+dnl ========================================================
dnl = Disable plugin support
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(plugins,
[ --disable-plugins Disable plugins support],
MOZ_PLUGINS=,
MOZ_PLUGINS=1)
dnl ========================================================
@@ -6710,19 +6753,17 @@ if test "$MOZ_MEMORY"; then
fi
if test ! -d "$WIN32_CRT_SRC_DIR"; then
AC_MSG_ERROR([Invalid Win32 CRT source directory: ${WIN32_CRT_SRC_DIR}])
fi
WIN32_CRT_SRC_DIR=`cd "$WIN32_CRT_SRC_DIR" && pwd -W`
_objdir_win=`pwd -W`
WIN32_CUSTOM_CRT_DIR="$_objdir_win/memory/jemalloc/crtsrc/build/intel"
- dnl Statically link the C++ stdlib. We only use this for Breakpad anyway.
- AC_DEFINE(_STATIC_CPPLIB)
- MOZ_MEMORY_LDFLAGS="-MANIFEST:NO -LIBPATH:\"$WIN32_CUSTOM_CRT_DIR\" -NODEFAULTLIB:msvcrt -NODEFAULTLIB:msvcrtd -DEFAULTLIB:mozcrt19"
+ MOZ_MEMORY_LDFLAGS="-MANIFEST:NO -LIBPATH:\"$WIN32_CUSTOM_CRT_DIR\" -NODEFAULTLIB:msvcrt -NODEFAULTLIB:msvcrtd -NODEFAULTLIB:msvcprt -NODEFAULTLIB:msvcprtd -DEFAULTLIB:mozcrt19 -DEFAULTLIB:mozcpp19"
dnl Also pass this to NSPR/NSS
DLLFLAGS="$DLLFLAGS $MOZ_MEMORY_LDFLAGS"
export DLLFLAGS
;;
*wince)
AC_DEFINE(MOZ_MEMORY_WINCE)
AC_DEFINE(MOZ_MEMORY_WINDOWS)
if test -z "$WINCE_WINDOWS_MOBILE"; then
@@ -7373,16 +7414,17 @@ if test "$ac_nscap_nonconst_opeq_bug" =
AC_DEFINE(NSCAP_DONT_PROVIDE_NONCONST_OPEQ)
fi
fi # SKIP_COMPILER_CHECKS
dnl ========================================================
dnl C++ rtti
dnl Should be smarter and check that the compiler does indeed have rtti
dnl ========================================================
+
MOZ_ARG_ENABLE_BOOL(cpp-rtti,
[ --enable-cpp-rtti Enable C++ RTTI ],
[ _MOZ_USE_RTTI=1 ],
[ _MOZ_USE_RTTI= ])
if test "$_MOZ_USE_RTTI"; then
_MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_ON
else
@@ -7481,34 +7523,33 @@ dnl =
dnl ========================================================
MOZ_ARG_HEADER(Static build options)
MOZ_ARG_ENABLE_BOOL(static,
[ --enable-static Enable building of internal static libs],
BUILD_STATIC_LIBS=1,
BUILD_STATIC_LIBS=)
-dnl Disable libxul in debug builds, but not for xulrunner.
-if test -n "$MOZ_DEBUG" -a "$MOZ_BUILD_APP" != "xulrunner"; then
- MOZ_ENABLE_LIBXUL=
-fi
-
MOZ_ARG_ENABLE_BOOL(libxul,
[ --enable-libxul Enable building of libxul],
MOZ_ENABLE_LIBXUL=1,
MOZ_ENABLE_LIBXUL=)
if test -n "$MOZ_STATIC_BUILD_UNSUPPORTED" -a -n "$BUILD_STATIC_LIBS"; then
AC_MSG_ERROR([--enable-static is not supported for building $MOZ_APP_NAME. You probably want --enable-libxul.])
fi
if test -n "$MOZ_ENABLE_LIBXUL" -a -n "$BUILD_STATIC_LIBS"; then
AC_MSG_ERROR([--enable-libxul is not compatible with --enable-static])
fi
+if test -n "$MOZ_IPC" -a -z "$MOZ_ENABLE_LIBXUL"; then
+ AC_MSG_ERROR([--enable-ipc requires --enable-libxul])
+fi
+
AC_SUBST(LIBXUL_LIBS)
if test -n "$MOZ_ENABLE_LIBXUL"; then
XPCOM_LIBS="$LIBXUL_LIBS"
AC_DEFINE(MOZ_ENABLE_LIBXUL)
else
if test -n "$BUILD_STATIC_LIBS"; then
AC_DEFINE(MOZ_STATIC_BUILD)
@@ -7798,17 +7839,17 @@ if test "$MOZ_TREE_CAIRO"; then
MOZ_CAIRO_LIBS="$MOZ_CAIRO_LIBS $XLDFLAGS -lXrender -lfreetype -lfontconfig"
fi
fi
CAIRO_FEATURES_H=gfx/cairo/cairo/src/cairo-features.h
mv -f $CAIRO_FEATURES_H "$CAIRO_FEATURES_H".orig 2> /dev/null
else
- PKG_CHECK_MODULES(CAIRO, cairo >= $CAIRO_VERSION freetype2 fontconfig)
+ PKG_CHECK_MODULES(CAIRO, cairo >= $CAIRO_VERSION pixman-1 freetype2 fontconfig)
MOZ_CAIRO_CFLAGS=$CAIRO_CFLAGS
MOZ_CAIRO_LIBS=$CAIRO_LIBS
if test "$MOZ_X11"; then
PKG_CHECK_MODULES(CAIRO_XRENDER, cairo-xlib-xrender >= $CAIRO_VERSION)
MOZ_CAIRO_LIBS="$MOZ_CAIRO_LIBS $XLDFLAGS $CAIRO_XRENDER_LIBS"
MOZ_CAIRO_CFLAGS="$MOZ_CAIRO_CFLAGS $CAIRO_XRENDER_CFLAGS"
fi
fi
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -34,16 +34,17 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIObjectFrame;
interface nsIPluginInstance;
+interface nsIPluginTag;
interface nsIDOMElement;
interface nsIDOMClientRect;
/**
* This interface represents a content node that loads objects.
*/
[scriptable, uuid(90ab443e-3e99-405e-88c9-9c42adaa3217)]
interface nsIObjectLoadingContent : nsISupports
@@ -75,17 +76,16 @@ interface nsIObjectLoadingContent : nsIS
/**
* Returns the plugin instance if it has already been instantiated. This
* will never instantiate the plugin and so is safe to call even when
* content script must not execute.
*/
[noscript] readonly attribute nsIPluginInstance pluginInstance;
-
/**
* Makes sure that a frame for this object exists, and that the plugin is
* instantiated. This method does nothing if the type is not #TYPE_PLUGIN.
* There is no guarantee that there will be a frame after this method is
* called; for example, the node may have a display:none style. If plugin
* instantiation is possible, it will be done synchronously by this method,
* and the plugin instance will be returned. A success return value does not
* necessarily mean that the instance is nonnull.
@@ -107,19 +107,24 @@ interface nsIObjectLoadingContent : nsIS
* because nsIObjectFrame is unscriptable.
*/
[noscript] void hasNewFrame(in nsIObjectFrame aFrame);
};
/**
* This interface extends the nsIObjectLoadingContent for the 1.9.2 branch
*/
-[scriptable, uuid(f91247a2-fb8b-42d3-a9b6-8f1f49685c43)]
+[scriptable, uuid(2725a137-db4b-4e43-a096-a084aeaa8b0b)]
interface nsIObjectLoadingContent_MOZILLA_1_9_2_BRANCH : nsISupports
{
/**
* Tells the object to paint directly in this location ignoring any
* positioning information that may have been provided otherwise
*/
void setAbsoluteScreenPosition(in nsIDOMElement element,
in nsIDOMClientRect position,
in nsIDOMClientRect clip);
+
+ [noscript] void pluginCrashed(in nsIPluginTag pluginTag,
+ in AString pluginDumpID,
+ in AString browserDumpID,
+ in boolean submittedCrashReport);
};
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -39,26 +39,31 @@
/*
* A base class implementing nsIObjectLoadingContent for use by
* various content nodes that want to provide plugin/document/image
* loading functionality (eg <embed>, <object>, <applet>, etc).
*/
// Interface headers
#include "imgILoader.h"
+#include "nsEventDispatcher.h"
#include "nsIContent.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
+#include "nsIDOMDataContainerEvent.h"
+#include "nsIDOMDocumentEvent.h"
+#include "nsIDOMEventTarget.h"
#include "nsIExternalProtocolHandler.h"
#include "nsIEventStateManager.h"
#include "nsIObjectFrame.h"
#include "nsIPluginDocument.h"
#include "nsIPluginHost.h"
#include "nsIPluginInstance.h"
#include "nsIPresShell.h"
+#include "nsIPrivateDOMEvent.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsIStreamConverterService.h"
#include "nsIURILoader.h"
#include "nsIURL.h"
#include "nsIWebNavigation.h"
#include "nsIWebNavigationInfo.h"
#include "nsIScriptChannel.h"
@@ -75,16 +80,17 @@
#include "nsContentUtils.h"
#include "nsDocShellCID.h"
#include "nsGkAtoms.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsPresShellIterator.h"
#include "nsMimeTypes.h"
#include "nsStyleUtil.h"
+#include "nsGUIEvent.h"
// Concrete classes
#include "nsFrameLoader.h"
#include "nsObjectLoadingContent.h"
#include "mozAutoDocUpdate.h"
#ifdef PR_LOGGING
@@ -205,16 +211,113 @@ nsPluginErrorEvent::Run()
return NS_OK;
}
nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
type, PR_TRUE, PR_TRUE);
return NS_OK;
}
+/**
+ * A task for firing PluginCrashed DOM Events.
+ */
+class nsPluginCrashedEvent : public nsRunnable {
+public:
+ nsCOMPtr<nsIContent> mContent;
+ nsString mPluginDumpID;
+ nsString mBrowserDumpID;
+ nsString mPluginName;
+ PRBool mSubmittedCrashReport;
+
+ nsPluginCrashedEvent(nsIContent* aContent,
+ const nsAString& aPluginDumpID,
+ const nsAString& aBrowserDumpID,
+ const nsAString& aPluginName,
+ PRBool submittedCrashReport)
+ : mContent(aContent),
+ mPluginDumpID(aPluginDumpID),
+ mBrowserDumpID(aBrowserDumpID),
+ mPluginName(aPluginName),
+ mSubmittedCrashReport(submittedCrashReport)
+ {}
+
+ ~nsPluginCrashedEvent() {}
+
+ NS_IMETHOD Run();
+};
+
+NS_IMETHODIMP
+nsPluginCrashedEvent::Run()
+{
+ LOG(("OBJLC []: Firing plugin crashed event for content %p\n",
+ mContent.get()));
+
+ nsCOMPtr<nsIDOMDocumentEvent> domEventDoc =
+ do_QueryInterface(mContent->GetDocument());
+ if (!domEventDoc) {
+ NS_WARNING("Couldn't get document for PluginCrashed event!");
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMEvent> event;
+ domEventDoc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"),
+ getter_AddRefs(event));
+ nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
+ nsCOMPtr<nsIDOMDataContainerEvent> containerEvent(do_QueryInterface(event));
+ if (!privateEvent || !containerEvent) {
+ NS_WARNING("Couldn't QI event for PluginCrashed event!");
+ return NS_OK;
+ }
+
+ event->InitEvent(NS_LITERAL_STRING("PluginCrashed"), PR_TRUE, PR_TRUE);
+ privateEvent->SetTrusted(PR_TRUE);
+ privateEvent->GetInternalNSEvent()->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
+
+ nsCOMPtr<nsIWritableVariant> variant;
+
+ // add a "pluginDumpID" property to this event
+ variant = do_CreateInstance("@mozilla.org/variant;1");
+ if (!variant) {
+ NS_WARNING("Couldn't create pluginDumpID variant for PluginCrashed event!");
+ return NS_OK;
+ }
+ variant->SetAsAString(mPluginDumpID);
+ containerEvent->SetData(NS_LITERAL_STRING("pluginDumpID"), variant);
+
+ // add a "browserDumpID" property to this event
+ variant = do_CreateInstance("@mozilla.org/variant;1");
+ if (!variant) {
+ NS_WARNING("Couldn't create browserDumpID variant for PluginCrashed event!");
+ return NS_OK;
+ }
+ variant->SetAsAString(mBrowserDumpID);
+ containerEvent->SetData(NS_LITERAL_STRING("browserDumpID"), variant);
+
+ // add a "pluginName" property to this event
+ variant = do_CreateInstance("@mozilla.org/variant;1");
+ if (!variant) {
+ NS_WARNING("Couldn't create pluginName variant for PluginCrashed event!");
+ return NS_OK;
+ }
+ variant->SetAsAString(mPluginName);
+ containerEvent->SetData(NS_LITERAL_STRING("pluginName"), variant);
+
+ // add a "submittedCrashReport" property to this event
+ variant = do_CreateInstance("@mozilla.org/variant;1");
+ if (!variant) {
+ NS_WARNING("Couldn't create crashSubmit variant for PluginCrashed event!");
+ return NS_OK;
+ }
+ variant->SetAsBool(mSubmittedCrashReport);
+ containerEvent->SetData(NS_LITERAL_STRING("submittedCrashReport"), variant);
+
+ nsEventDispatcher::DispatchDOMEvent(mContent, nsnull, event, nsnull, nsnull);
+ return NS_OK;
+}
+
class AutoNotifier {
public:
AutoNotifier(nsObjectLoadingContent* aContent, PRBool aNotify) :
mContent(aContent), mNotify(aNotify) {
mOldType = aContent->Type();
mOldState = aContent->ObjectState();
}
~AutoNotifier() {
@@ -251,17 +354,17 @@ class AutoFallback {
public:
AutoFallback(nsObjectLoadingContent* aContent, const nsresult* rv)
: mContent(aContent), mResult(rv), mPluginState(ePluginOtherState) {}
~AutoFallback() {
if (NS_FAILED(*mResult)) {
LOG(("OBJLC [%p]: rv=%08x, falling back\n", mContent, *mResult));
mContent->Fallback(PR_FALSE);
if (mPluginState != ePluginOtherState) {
- mContent->mPluginState = mPluginState;
+ mContent->mFallbackReason = mPluginState;
}
}
}
/**
* This should be set to something other than ePluginOtherState to indicate
* a specific failure that should be passed on.
*/
@@ -354,17 +457,17 @@ IsPluginEnabledByExtension(nsIURI* uri,
nsObjectLoadingContent::nsObjectLoadingContent()
: mPendingInstantiateEvent(nsnull)
, mChannel(nsnull)
, mType(eType_Loading)
, mInstantiating(PR_FALSE)
, mUserDisabled(PR_FALSE)
, mSuppressed(PR_FALSE)
- , mPluginState(ePluginOtherState)
+ , mFallbackReason(ePluginOtherState)
{
}
nsObjectLoadingContent::~nsObjectLoadingContent()
{
DestroyImageLoadingContent();
if (mFrameLoader) {
mFrameLoader->Destroy();
@@ -593,26 +696,26 @@ nsObjectLoadingContent::OnStartRequest(n
}
break;
case eType_Loading:
NS_NOTREACHED("Should not have a loading type here!");
case eType_Null:
LOG(("OBJLC [%p]: Unsupported type, falling back\n", this));
// Need to fallback here (instead of using the case below), so that we can
- // set mPluginState without it being overwritten. This is also why we
+ // set mFallbackReason without it being overwritten. This is also why we
// return early.
Fallback(PR_FALSE);
PluginSupportState pluginState = GetPluginSupportState(thisContent,
mContentType);
// Do nothing, but fire the plugin not found event if needed
if (pluginState != ePluginOtherState) {
+ mFallbackReason = pluginState;
FirePluginError(thisContent, pluginState);
- mPluginState = pluginState;
}
return NS_BINDING_ABORTED;
}
if (mFinalListener) {
mType = newType;
rv = mFinalListener->OnStartRequest(aRequest, aContext);
if (NS_FAILED(rv)) {
@@ -926,23 +1029,26 @@ nsObjectLoadingContent::ObjectState() co
case eType_Null:
if (mSuppressed)
return NS_EVENT_STATE_SUPPRESSED;
if (mUserDisabled)
return NS_EVENT_STATE_USERDISABLED;
// Otherwise, broken
PRInt32 state = NS_EVENT_STATE_BROKEN;
- switch (mPluginState) {
+ switch (mFallbackReason) {
case ePluginDisabled:
state |= NS_EVENT_STATE_HANDLER_DISABLED;
break;
case ePluginBlocklisted:
state |= NS_EVENT_STATE_HANDLER_BLOCKED;
break;
+ case ePluginCrashed:
+ state |= NS_EVENT_STATE_HANDLER_CRASHED;
+ break;
case ePluginUnsupported:
state |= NS_EVENT_STATE_TYPE_UNSUPPORTED;
break;
}
return state;
};
NS_NOTREACHED("unknown type?");
// this return statement only exists to avoid a compile warning
@@ -1481,17 +1587,17 @@ nsObjectLoadingContent::UnloadContent()
// Don't notify in CancelImageRequests. We do it ourselves.
CancelImageRequests(PR_FALSE);
if (mFrameLoader) {
mFrameLoader->Destroy();
mFrameLoader = nsnull;
}
mType = eType_Null;
mUserDisabled = mSuppressed = PR_FALSE;
- mPluginState = ePluginOtherState;
+ mFallbackReason = ePluginOtherState;
}
void
nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
PRInt32 aOldState,
PRBool aSync)
{
LOG(("OBJLC [%p]: Notifying about state change: (%u, %x) -> (%u, %x) (sync=%i)\n",
@@ -1880,9 +1986,35 @@ nsObjectLoadingContent::SetAbsoluteScree
{
nsIObjectFrame* frame = GetExistingFrame(eFlushLayout);
if (!frame)
return NS_ERROR_NOT_AVAILABLE;
return frame->SetAbsoluteScreenPosition(element, position, clip);
}
+NS_IMETHODIMP
+nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
+ const nsAString& pluginDumpID,
+ const nsAString& browserDumpID,
+ PRBool submittedCrashReport)
+{
+ AutoNotifier notifier(this, PR_TRUE);
+ UnloadContent();
+ mFallbackReason = ePluginCrashed;
+ nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+ // Note that aPluginTag in invalidated after we're called, so copy
+ // out any data we need now.
+ nsCAutoString pluginName;
+ aPluginTag->GetName(pluginName);
+
+ nsCOMPtr<nsIRunnable> ev = new nsPluginCrashedEvent(thisContent,
+ pluginDumpID,
+ browserDumpID,
+ NS_ConvertUTF8toUTF16(pluginName),
+ submittedCrashReport);
+ nsresult rv = NS_DispatchToCurrentThread(ev);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to dispatch nsPluginCrashedEvent");
+ }
+ return NS_OK;
+}
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -55,23 +55,22 @@
#include "nsIChannelClassifier.h"
class nsAsyncInstantiateEvent;
class AutoNotifier;
class AutoFallback;
class AutoSetInstantiatingToFalse;
enum PluginSupportState {
- ePluginUnsupported, // The plugin is not supported (not installed, say)
- ePluginDisabled, // The plugin has been explicitly disabled by the
- // user.
+ ePluginUnsupported, // The plugin is not supported (e.g. not installed)
+ ePluginDisabled, // The plugin has been explicitly disabled by the user
ePluginBlocklisted, // The plugin is blocklisted and disabled
ePluginOutdated, // The plugin is considered outdated, but not disabled
- ePluginOtherState // Something else (e.g. not a plugin at all as far
- // as we can tell).
+ ePluginOtherState, // Something else (e.g. uninitialized or not a plugin)
+ ePluginCrashed
};
/**
* INVARIANTS OF THIS CLASS
* - mChannel is non-null between asyncOpen and onStopRequest (NOTE: Only needs
* to be valid until onStopRequest is called on mFinalListener, not
* necessarily until the channel calls onStopRequest on us)
* - mChannel corresponds to the channel that gets passed to the
@@ -417,15 +416,15 @@ class nsObjectLoadingContent : public ns
* Whether we are about to call instantiate on our frame. If we aren't,
* SetFrame needs to asynchronously call Instantiate.
*/
PRBool mInstantiating : 1;
// Blocking status from content policy
PRBool mUserDisabled : 1;
PRBool mSuppressed : 1;
// A specific state that caused us to fallback
- PluginSupportState mPluginState;
+ PluginSupportState mFallbackReason;
friend class nsAsyncInstantiateEvent;
};
#endif
--- a/content/events/public/nsIEventStateManager.h
+++ b/content/events/public/nsIEventStateManager.h
@@ -192,9 +192,13 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIEventSt
#define NS_EVENT_STATE_HANDLER_BLOCKED \
0x01000000
// Handler for the content has been disabled
#define NS_EVENT_STATE_HANDLER_DISABLED \
0x02000000
#define NS_EVENT_STATE_INDETERMINATE 0x04000000 // CSS3-Selectors
+// Handler for the content has crashed
+#define NS_EVENT_STATE_HANDLER_CRASHED \
+ 0x08000000
+
#endif // nsIEventStateManager_h__
--- a/content/events/src/nsDOMMouseEvent.cpp
+++ b/content/events/src/nsDOMMouseEvent.cpp
@@ -49,16 +49,19 @@ nsDOMMouseEvent::nsDOMMouseEvent(nsPresC
new nsMouseEvent(PR_FALSE, 0, nsnull,
nsMouseEvent::eReal))
{
// There's no way to make this class' ctor allocate an nsMouseScrollEvent.
// It's not that important, though, since a scroll event is not a real
// DOM event.
if (aEvent) {
+ NS_ASSERTION(static_cast<nsMouseEvent*>(mEvent)->reason
+ != nsMouseEvent::eSynthesized,
+ "Don't dispatch DOM events from synthesized mouse events");
mEventIsInternal = PR_FALSE;
}
else {
mEventIsInternal = PR_TRUE;
mEvent->time = PR_Now();
mEvent->refPoint.x = mEvent->refPoint.y = 0;
}
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -183,16 +183,25 @@ static nsITimerCallback* gUserInteractio
// Pixel scroll accumulation for synthetic line scrolls
static nscoord gPixelScrollDeltaX = 0;
static nscoord gPixelScrollDeltaY = 0;
static PRUint32 gPixelScrollDeltaTimeout = 0;
static nscoord
GetScrollableViewLineHeight(nsPresContext* aPresContext, nsIFrame* aTargetFrame);
+static inline PRBool
+IsMouseEventReal(nsEvent* aEvent)
+{
+ NS_ABORT_IF_FALSE(aEvent->eventStructType == NS_MOUSE_EVENT,
+ "Not a mouse event");
+ // Return true if not synthesized.
+ return static_cast<nsMouseEvent*>(aEvent)->reason == nsMouseEvent::eReal;
+}
+
#ifdef DEBUG_DOCSHELL_FOCUS
static void
PrintDocTree(nsIDocShellTreeItem* aParentItem, int aLevel)
{
for (PRInt32 i=0;i<aLevel;i++) printf(" ");
PRInt32 childWebshellCount;
aParentItem->GetChildCount(&childWebshellCount);
@@ -502,17 +511,17 @@ nsMouseWheelTransaction::OnEvent(nsEvent
OutOfTime(sMouseMoved, GetIgnoreMoveDelayTime())) {
// Terminate the current mousewheel transaction if the mouse moved more
// than ignoremovedelay milliseconds ago
EndTransaction();
}
return;
case NS_MOUSE_MOVE:
case NS_DRAGDROP_OVER:
- if (((nsMouseEvent*)aEvent)->reason == nsMouseEvent::eReal) {
+ if (IsMouseEventReal(aEvent)) {
// If the cursor is moving to be outside the frame,
// terminate the scrollwheel transaction.
nsIntPoint pt = GetScreenPoint((nsGUIEvent*)aEvent);
nsIntRect r = sTargetFrame->GetScreenRectExternal();
if (!r.Contains(pt)) {
EndTransaction();
return;
}
@@ -1024,17 +1033,17 @@ nsEventStateManager::PreHandleEvent(nsPr
NS_ASSERTION(mCurrentTarget, "mCurrentTarget is null. this should not happen. see bug #13007");
if (!mCurrentTarget) return NS_ERROR_NULL_POINTER;
}
// Do not take account NS_MOUSE_ENTER/EXIT so that loading a page
// when user is not active doesn't change the state to active.
if (NS_IS_TRUSTED_EVENT(aEvent) &&
((aEvent->eventStructType == NS_MOUSE_EVENT &&
- static_cast<nsMouseEvent*>(aEvent)->reason == nsMouseEvent::eReal &&
+ IsMouseEventReal(aEvent) &&
aEvent->message != NS_MOUSE_ENTER &&
aEvent->message != NS_MOUSE_EXIT) ||
aEvent->eventStructType == NS_MOUSE_SCROLL_EVENT ||
aEvent->eventStructType == NS_KEY_EVENT)) {
if (gMouseOrKeyboardEventCounter == 0) {
nsCOMPtr<nsIObserverService> obs =
do_GetService("@mozilla.org/observer-service;1");
if (obs) {
@@ -2923,23 +2932,25 @@ nsEventStateManager::PostHandleEvent(nsP
// any of our own processing of a drag. Workaround for bug 43258.
StopTrackingDragGesture();
}
}
break;
case NS_MOUSE_BUTTON_UP:
{
SetContentState(nsnull, NS_EVENT_STATE_ACTIVE);
- if (!mCurrentTarget) {
- nsIFrame* targ;
- GetEventTarget(&targ);
- }
- if (mCurrentTarget) {
- ret =
- CheckForAndDispatchClick(presContext, (nsMouseEvent*)aEvent, aStatus);
+ if (IsMouseEventReal(aEvent)) {
+ if (!mCurrentTarget) {
+ nsIFrame* targ;
+ GetEventTarget(&targ);
+ }
+ if (mCurrentTarget) {
+ ret = CheckForAndDispatchClick(presContext, (nsMouseEvent*)aEvent,
+ aStatus);
+ }
}
nsIPresShell *shell = presContext->GetPresShell();
if (shell) {
nsIViewManager* viewMan = shell->GetViewManager();
if (viewMan) {
nsIView* grabbingView = nsnull;
viewMan->GetMouseEventGrabber(grabbingView);
if (grabbingView == aView) {
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -74,16 +74,17 @@ ifdef MOZ_SMIL
DIRS += interfaces/smil
endif
DIRS += \
public/coreEvents \
base \
src \
locales \
+ plugins \
$(NULL)
ifdef ENABLE_TESTS
DIRS += tests
endif
include $(topsrcdir)/config/rules.mk
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -3593,16 +3593,17 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMModalContentWindow)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(DataContainerEvent, nsIDOMDataContainerEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataContainerEvent)
+ DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(MessageEvent, nsIDOMMessageEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMessageEvent)
DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(GeoGeolocation, nsIDOMGeoGeolocation)
@@ -9748,17 +9749,17 @@ nsHTMLPluginObjElementSH::Call(nsIXPConn
jsval *argv, jsval *vp, PRBool *_retval)
{
nsCOMPtr<nsIPluginInstance> pi;
nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, getter_AddRefs(pi));
NS_ENSURE_SUCCESS(rv, rv);
// If obj is a native wrapper, or if there's no plugin around for
// this object, throw.
- if (!ObjectIsNativeWrapper(cx, obj) || !pi) {
+ if (ObjectIsNativeWrapper(cx, obj) || !pi) {
return NS_ERROR_NOT_AVAILABLE;
}
JSObject *pi_obj = nsnull;
JSObject *pi_proto = nsnull;
rv = GetPluginJSObject(cx, obj, pi, &pi_obj, &pi_proto);
NS_ENSURE_SUCCESS(rv, rv);
new file mode 100644
--- /dev/null
+++ b/dom/plugins/AStream.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation <http://www.mozilla.org/>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_plugins_AStream_h
+#define mozilla_plugins_AStream_h
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * When we are passed NPStream->{ndata,pdata} in {NPN,NPP}_DestroyStream, we
+ * don't know whether it's a plugin stream or a browser stream. This abstract
+ * class lets us cast to the right type of object and send the appropriate
+ * message.
+ */
+class AStream
+{
+public:
+ virtual bool IsBrowserStream() = 0;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/plugins/BrowserStreamChild.cpp
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "BrowserStreamChild.h"
+#include "PluginInstanceChild.h"
+#include "StreamNotifyChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ StreamNotifyChild* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t* stype)
+ : mInstance(instance)
+ , mStreamStatus(kStreamOpen)
+ , mDestroyPending(NOT_DESTROYED)
+ , mNotifyPending(false)
+ , mInstanceDying(false)
+ , mState(CONSTRUCTING)
+ , mURL(url)
+ , mHeaders(headers)
+ , mStreamNotify(notifyData)
+ , mDeliveryTracker(this)
+{
+ PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s, %s)", FULLFUNCTION,
+ url.get(), length, lastmodified, (void*) notifyData,
+ headers.get(), mimeType.get()));
+
+ AssertPluginThread();
+
+ memset(&mStream, 0, sizeof(mStream));
+ mStream.ndata = static_cast<AStream*>(this);
+ mStream.url = NullableStringGet(mURL);
+ mStream.end = length;
+ mStream.lastmodified = lastmodified;
+ mStream.headers = NullableStringGet(mHeaders);
+ if (notifyData)
+ mStream.notifyData = notifyData->mClosure;
+}
+
+NPError
+BrowserStreamChild::StreamConstructed(
+ const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype)
+{
+ NPError rv = NPERR_NO_ERROR;
+
+ *stype = NP_NORMAL;
+ rv = mInstance->mPluginIface->newstream(
+ &mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
+ &mStream, seekable, stype);
+ if (rv != NPERR_NO_ERROR) {
+ mState = DELETING;
+ mStreamNotify = NULL;
+ }
+ else {
+ mState = ALIVE;
+
+ if (mStreamNotify)
+ mStreamNotify->SetAssociatedStream(this);
+ }
+
+ return rv;
+}
+
+BrowserStreamChild::~BrowserStreamChild()
+{
+ NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
+}
+
+bool
+BrowserStreamChild::RecvWrite(const int32_t& offset,
+ const Buffer& data,
+ const uint32_t& newlength)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ AssertPluginThread();
+
+ if (ALIVE != mState)
+ NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
+
+ if (kStreamOpen != mStreamStatus)
+ return true;
+
+ mStream.end = newlength;
+
+ NS_ASSERTION(data.Length() > 0, "Empty data");
+
+ PendingData* newdata = mPendingData.AppendElement();
+ newdata->offset = offset;
+ newdata->data = data;
+ newdata->curpos = 0;
+
+ EnsureDeliveryPending();
+
+ return true;
+}
+
+bool
+BrowserStreamChild::AnswerNPP_StreamAsFile(const nsCString& fname)
+{
+ PLUGIN_LOG_DEBUG(("%s (fname=%s)", FULLFUNCTION, fname.get()));
+
+ AssertPluginThread();
+
+ if (ALIVE != mState)
+ NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?");
+
+ if (kStreamOpen != mStreamStatus)
+ return true;
+
+ mInstance->mPluginIface->asfile(&mInstance->mData, &mStream,
+ fname.get());
+ return true;
+}
+
+bool
+BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (ALIVE != mState)
+ NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
+
+ mState = DYING;
+ mDestroyPending = DESTROY_PENDING;
+ if (NPRES_DONE != reason)
+ mStreamStatus = reason;
+
+ EnsureDeliveryPending();
+ return true;
+}
+
+bool
+BrowserStreamChild::Recv__delete__()
+{
+ AssertPluginThread();
+
+ if (DELETING != mState)
+ NS_RUNTIMEABORT("Bad state, not DELETING");
+
+ return true;
+}
+
+NPError
+BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ AssertPluginThread();
+
+ if (ALIVE != mState || kStreamOpen != mStreamStatus)
+ return NPERR_GENERIC_ERROR;
+
+ IPCByteRanges ranges;
+ for (; aRangeList; aRangeList = aRangeList->next) {
+ IPCByteRange br = {aRangeList->offset, aRangeList->length};
+ ranges.push_back(br);
+ }
+
+ NPError result;
+ CallNPN_RequestRead(ranges, &result);
+ return result;
+}
+
+void
+BrowserStreamChild::NPN_DestroyStream(NPReason reason)
+{
+ mStreamStatus = reason;
+ if (ALIVE == mState)
+ SendNPN_DestroyStream(reason);
+
+ EnsureDeliveryPending();
+}
+
+void
+BrowserStreamChild::EnsureDeliveryPending()
+{
+ MessageLoop::current()->PostTask(FROM_HERE,
+ mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver));
+}
+
+void
+BrowserStreamChild::Deliver()
+{
+ while (kStreamOpen == mStreamStatus && mPendingData.Length()) {
+ if (DeliverPendingData() && kStreamOpen == mStreamStatus) {
+ SetSuspendedTimer();
+ return;
+ }
+ }
+ ClearSuspendedTimer();
+
+ NS_ASSERTION(kStreamOpen != mStreamStatus || 0 == mPendingData.Length(),
+ "Exit out of the data-delivery loop with pending data");
+ mPendingData.Clear();
+
+ if (DESTROY_PENDING == mDestroyPending) {
+ mDestroyPending = DESTROYED;
+ if (mState != DYING)
+ NS_RUNTIMEABORT("mDestroyPending but state not DYING");
+
+ NS_ASSERTION(NPRES_DONE != mStreamStatus, "Success status set too early!");
+ if (kStreamOpen == mStreamStatus)
+ mStreamStatus = NPRES_DONE;
+
+ (void) mInstance->mPluginIface
+ ->destroystream(&mInstance->mData, &mStream, mStreamStatus);
+ }
+ if (DESTROYED == mDestroyPending && mNotifyPending) {
+ NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?");
+
+ mNotifyPending = false;
+ mStreamNotify->NPP_URLNotify(mStreamStatus);
+ delete mStreamNotify;
+ mStreamNotify = NULL;
+ }
+ if (DYING == mState && DESTROYED == mDestroyPending
+ && !mStreamNotify && !mInstanceDying) {
+ SendStreamDestroyed();
+ mState = DELETING;
+ }
+}
+
+bool
+BrowserStreamChild::DeliverPendingData()
+{
+ if (mState != ALIVE && mState != DYING)
+ NS_RUNTIMEABORT("Unexpected state");
+
+ NS_ASSERTION(mPendingData.Length(), "Called from Deliver with empty pending");
+
+ while (mPendingData[0].curpos < mPendingData[0].data.Length()) {
+ int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
+ if (kStreamOpen != mStreamStatus)
+ return false;
+ if (0 == r) // plugin wants to suspend delivery
+ return true;
+
+ r = mInstance->mPluginIface->write(
+ &mInstance->mData, &mStream,
+ mPendingData[0].offset + mPendingData[0].curpos, // offset
+ mPendingData[0].data.Length() - mPendingData[0].curpos, // length
+ const_cast<char*>(mPendingData[0].data.BeginReading() + mPendingData[0].curpos));
+ if (kStreamOpen != mStreamStatus)
+ return false;
+ if (0 == r)
+ return true;
+ if (r < 0) { // error condition
+ NPN_DestroyStream(NPRES_NETWORK_ERR);
+ return false;
+ }
+ mPendingData[0].curpos += r;
+ }
+ mPendingData.RemoveElementAt(0);
+ return false;
+}
+
+void
+BrowserStreamChild::SetSuspendedTimer()
+{
+ if (mSuspendedTimer.IsRunning())
+ return;
+ mSuspendedTimer.Start(
+ base::TimeDelta::FromMilliseconds(100), // 100ms copied from Mozilla plugin host
+ this, &BrowserStreamChild::Deliver);
+}
+
+void
+BrowserStreamChild::ClearSuspendedTimer()
+{
+ mSuspendedTimer.Stop();
+}
+
+} /* namespace plugins */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/dom/plugins/BrowserStreamChild.h
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_plugins_BrowserStreamChild_h
+#define mozilla_plugins_BrowserStreamChild_h 1
+
+#include "mozilla/plugins/PBrowserStreamChild.h"
+#include "mozilla/plugins/AStream.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceChild;
+class StreamNotifyChild;
+
+class BrowserStreamChild : public PBrowserStreamChild, public AStream
+{
+public:
+ BrowserStreamChild(PluginInstanceChild* instance,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ StreamNotifyChild* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t* stype);
+ virtual ~BrowserStreamChild();
+
+ NS_OVERRIDE virtual bool IsBrowserStream() { return true; }
+
+ NPError StreamConstructed(
+ const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype);
+
+ virtual bool RecvWrite(const int32_t& offset,
+ const Buffer& data,
+ const uint32_t& newsize);
+ virtual bool AnswerNPP_StreamAsFile(const nsCString& fname);
+ virtual bool RecvNPP_DestroyStream(const NPReason& reason);
+ virtual bool Recv__delete__();
+
+ void EnsureCorrectInstance(PluginInstanceChild* i)
+ {
+ if (i != mInstance)
+ NS_RUNTIMEABORT("Incorrect stream instance");
+ }
+ void EnsureCorrectStream(NPStream* s)
+ {
+ if (s != &mStream)
+ NS_RUNTIMEABORT("Incorrect stream data");
+ }
+
+ NPError NPN_RequestRead(NPByteRange* aRangeList);
+ void NPN_DestroyStream(NPReason reason);
+
+ void NotifyPending() {
+ NS_ASSERTION(!mNotifyPending, "Pending twice?");
+ mNotifyPending = true;
+ EnsureDeliveryPending();
+ }
+
+ /**
+ * During instance destruction, artificially cancel all outstanding streams.
+ *
+ * @return false if we are already in the DELETING state.
+ */
+ bool InstanceDying() {
+ if (DELETING == mState)
+ return false;
+
+ mInstanceDying = true;
+ return true;
+ }
+
+ void FinishDelivery() {
+ NS_ASSERTION(mInstanceDying, "Should only be called after InstanceDying");
+ NS_ASSERTION(DELETING != mState, "InstanceDying didn't work?");
+ mStreamStatus = NPRES_USER_BREAK;
+ Deliver();
+ NS_ASSERTION(!mStreamNotify, "Didn't deliver NPN_URLNotify?");
+ }
+
+private:
+ friend class StreamNotifyChild;
+ using PBrowserStreamChild::SendNPN_DestroyStream;
+
+ /**
+ * Post an event to ensure delivery of pending data/destroy/urlnotify events
+ * outside of the current RPC stack.
+ */
+ void EnsureDeliveryPending();
+
+ /**
+ * Deliver data, destruction, notify scheduling
+ * or cancelling the suspended timer as needed.
+ */
+ void Deliver();
+
+ /**
+ * Deliver one chunk of pending data.
+ * @return true if the plugin indicated a pause was necessary
+ */
+ bool DeliverPendingData();
+
+ void SetSuspendedTimer();
+ void ClearSuspendedTimer();
+
+ PluginInstanceChild* mInstance;
+ NPStream mStream;
+
+ static const NPReason kStreamOpen = -1;
+
+ /**
+ * The plugin's notion of whether a stream has been "closed" (no more
+ * data delivery) differs from the plugin host due to asynchronous delivery
+ * of data and NPN_DestroyStream. While the plugin-visible stream is open,
+ * mStreamStatus should be kStreamOpen (-1). mStreamStatus will be a
+ * failure code if either the parent or child indicates stream failure.
+ */
+ NPReason mStreamStatus;
+
+ /**
+ * Delivery of NPP_DestroyStream and NPP_URLNotify must be postponed until
+ * all data has been delivered.
+ */
+ enum {
+ NOT_DESTROYED, // NPP_DestroyStream not yet received
+ DESTROY_PENDING, // NPP_DestroyStream received, not yet delivered
+ DESTROYED // NPP_DestroyStream delivered, NPP_URLNotify may still be pending
+ } mDestroyPending;
+ bool mNotifyPending;
+
+ // When NPP_Destroy is called for our instance (manager), this flag is set
+ // cancels the stream and avoids sending StreamDestroyed.
+ bool mInstanceDying;
+
+ enum {
+ CONSTRUCTING,
+ ALIVE,
+ DYING,
+ DELETING
+ } mState;
+ nsCString mURL;
+ nsCString mHeaders;
+ StreamNotifyChild* mStreamNotify;
+
+ struct PendingData
+ {
+ int32_t offset;
+ Buffer data;
+ int32_t curpos;
+ };
+ nsTArray<PendingData> mPendingData;
+
+ /**
+ * Asynchronous RecvWrite messages are never delivered to the plugin
+ * immediately, because that may be in the midst of an unexpected RPC
+ * stack frame. It instead posts a runnable using this tracker to cancel
+ * in case we are destroyed.
+ */
+ ScopedRunnableMethodFactory<BrowserStreamChild> mDeliveryTracker;
+ base::RepeatingTimer<BrowserStreamChild> mSuspendedTimer;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif /* mozilla_plugins_BrowserStreamChild_h */
new file mode 100644
--- /dev/null
+++ b/dom/plugins/BrowserStreamParent.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+
+#include "BrowserStreamParent.h"
+#include "PluginInstanceParent.h"
+
+// How much data are we willing to send across the wire
+// in one chunk?
+static const int32_t kSendDataChunk = 0x1000;
+
+namespace mozilla {
+namespace plugins {
+
+BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp,
+ NPStream* stream)
+ : mNPP(npp)
+ , mStream(stream)
+ , mState(ALIVE)
+{
+ mStream->pdata = static_cast<AStream*>(this);
+}
+
+BrowserStreamParent::~BrowserStreamParent()
+{
+}
+
+bool
+BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
+ NPError* result)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ switch (mState) {
+ case ALIVE:
+ break;
+
+ case DYING:
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+
+ default:
+ NS_ERROR("Unexpected state");
+ return false;
+ }
+
+ if (!mStream)
+ return false;
+
+ if (ranges.size() > PR_INT32_MAX)
+ return false;
+
+ nsAutoArrayPtr<NPByteRange> rp(new NPByteRange[ranges.size()]);
+ for (PRUint32 i = 0; i < ranges.size(); ++i) {
+ rp[i].offset = ranges[i].offset;
+ rp[i].length = ranges[i].length;
+ rp[i].next = &rp[i + 1];
+ }
+ rp[ranges.size() - 1].next = NULL;
+
+ *result = mNPP->mNPNIface->requestread(mStream, rp);
+ return true;
+}
+
+bool
+BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason)
+{
+ switch (mState) {
+ case ALIVE:
+ break;
+
+ case DYING:
+ return true;
+
+ default:
+ NS_ERROR("Unexpected state");
+ return false;
+ };
+
+ mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
+ return true;
+}
+
+void
+BrowserStreamParent::NPP_DestroyStream(NPReason reason)
+{
+ NS_ASSERTION(ALIVE == mState, "NPP_DestroyStream called twice?");
+ mState = DYING;
+ SendNPP_DestroyStream(reason);
+}
+
+bool
+BrowserStreamParent::RecvStreamDestroyed()
+{
+ if (DYING != mState) {
+ NS_ERROR("Unexpected state");
+ return false;
+ }
+
+ mState = DELETING;
+ Send__delete__(this);
+ return true;
+}
+
+int32_t
+BrowserStreamParent::WriteReady()
+{
+ return kSendDataChunk;
+}
+
+int32_t
+BrowserStreamParent::Write(int32_t offset,
+ int32_t len,
+ void* buffer)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ NS_ASSERTION(ALIVE == mState, "Sending data after NPP_DestroyStream?");
+ NS_ASSERTION(len > 0, "Non-positive length to NPP_Write");
+
+ if (len > kSendDataChunk)
+ len = kSendDataChunk;
+
+ SendWrite(offset,
+ nsCString(static_cast<char*>(buffer), len),
+ mStream->end);
+
+ return len;
+}
+
+void
+BrowserStreamParent::StreamAsFile(const char* fname)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ NS_ASSERTION(ALIVE == mState,
+ "Calling streamasfile after NPP_DestroyStream?");
+
+ CallNPP_StreamAsFile(nsCString(fname));
+ return;
+}
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/BrowserStreamParent.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation <http://www.mozilla.org/>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_plugins_BrowserStreamParent_h
+#define mozilla_plugins_BrowserStreamParent_h
+
+#include "mozilla/plugins/PBrowserStreamParent.h"
+#include "mozilla/plugins/AStream.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceParent;
+
+class BrowserStreamParent : public PBrowserStreamParent, public AStream
+{
+ friend class PluginModuleParent;
+ friend class PluginInstanceParent;
+
+public:
+ BrowserStreamParent(PluginInstanceParent* npp,
+ NPStream* stream);
+ virtual ~BrowserStreamParent();
+
+ NS_OVERRIDE virtual bool IsBrowserStream() { return true; }
+
+ virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges,
+ NPError* result);
+
+ virtual bool RecvNPN_DestroyStream(const NPReason& reason);
+
+ virtual bool RecvStreamDestroyed();
+
+ int32_t WriteReady();
+ int32_t Write(int32_t offset, int32_t len, void* buffer);
+ void StreamAsFile(const char* fname);
+
+ void NPP_DestroyStream(NPReason reason);
+
+private:
+ using PBrowserStreamParent::SendNPP_DestroyStream;
+
+ PluginInstanceParent* mNPP;
+ NPStream* mStream;
+
+ enum {
+ ALIVE,
+ DYING,
+ DELETING
+ } mState;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ChildAsyncCall.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Firefox.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation <http://www.mozilla.org>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ChildAsyncCall.h"
+#include "PluginInstanceChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+ChildAsyncCall::ChildAsyncCall(PluginInstanceChild* instance,
+ PluginThreadCallback aFunc, void* aUserData)
+ : mInstance(instance)
+ , mFunc(aFunc)
+ , mData(aUserData)
+{
+}
+
+void
+ChildAsyncCall::Cancel()
+{
+ mInstance = NULL;
+ mFunc = NULL;
+ mData = NULL;
+}
+
+void
+ChildAsyncCall::Run()
+{
+ if (mFunc) {
+ mInstance->mPendingAsyncCalls.RemoveElement(this);
+ mFunc(mData);
+ }
+}
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ChildAsyncCall.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Firefox.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation <http://www.mozilla.org>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_plugins_ChildAsyncCall_h
+#define mozilla_plugins_ChildAsyncCall_h
+
+#include "PluginMessageUtils.h"
+#include "base/task.h"
+
+namespace mozilla {
+namespace plugins {
+
+typedef void (*PluginThreadCallback)(void*);
+
+class PluginInstanceChild;
+
+class ChildAsyncCall : public CancelableTask
+{
+public:
+ ChildAsyncCall(PluginInstanceChild* instance,
+ PluginThreadCallback aFunc, void* aUserData);
+
+ NS_OVERRIDE void Run();
+ NS_OVERRIDE void Cancel();
+
+private:
+ PluginInstanceChild* mInstance;
+ PluginThreadCallback mFunc;
+ void* mData;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_ChildAsyncCall_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/Makefile.in
@@ -0,0 +1,130 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Plugins.
+#
+# The Initial Developer of the Original Code is
+# Ben Turner <bent.mozilla@gmail.com>.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = dom
+
+EXPORTS_NAMESPACES = mozilla
+
+EXPORTS_mozilla = \
+ PluginLibrary.h \
+ PluginPRLibrary.h \
+ $(NULL)
+
+ifdef MOZ_IPC
+
+EXPORTS_NAMESPACES = mozilla mozilla/plugins
+
+EXPORTS_mozilla/plugins = \
+ BrowserStreamChild.h \
+ BrowserStreamParent.h \
+ ChildAsyncCall.h \
+ NPEventOSX.h \
+ NPEventWindows.h \
+ NPEventX11.h \
+ PluginIdentifierChild.h \
+ PluginIdentifierParent.h \
+ PluginInstanceChild.h \
+ PluginInstanceParent.h \
+ PluginMessageUtils.h \
+ PluginModuleChild.h \
+ PluginModuleParent.h \
+ PluginProcessParent.h \
+ PluginScriptableObjectChild.h \
+ PluginScriptableObjectParent.h \
+ PluginScriptableObjectUtils.h \
+ PluginScriptableObjectUtils-inl.h \
+ PluginInstanceChild.h \
+ PluginInstanceParent.h \
+ AStream.h \
+ BrowserStreamChild.h \
+ BrowserStreamParent.h \
+ PluginStreamChild.h \
+ PluginStreamParent.h \
+ PluginMessageUtils.h \
+ PluginProcessParent.h \
+ PluginThreadChild.h \
+ StreamNotifyChild.h \
+ StreamNotifyParent.h \
+ $(NULL)
+
+MODULE = dom
+LIBRARY_NAME = domplugins_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+EXPORT_LIBRARY = 1
+ENABLE_CXX_EXCEPTIONS = 1
+
+CPPSRCS = \
+ ChildAsyncCall.cpp \
+ PluginMessageUtils.cpp \
+ PluginInstanceChild.cpp \
+ PluginInstanceParent.cpp \
+ PluginModuleChild.cpp \
+ PluginModuleParent.cpp \
+ PluginProcessParent.cpp \
+ PluginScriptableObjectChild.cpp \
+ PluginScriptableObjectParent.cpp \
+ BrowserStreamChild.cpp \
+ BrowserStreamParent.cpp \
+ PluginStreamChild.cpp \
+ PluginStreamParent.cpp \
+ PluginThreadChild.cpp \
+ $(NULL)
+
+LOCAL_INCLUDES = \
+ -I$(topsrcdir)/modules/plugin/base/public/ \
+ -I$(topsrcdir)/modules/plugin/base/src/ \
+ -I$(topsrcdir)/toolkit/xre \
+ -I$(topsrcdir)/toolkit/crashreporter \
+ $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+CXXFLAGS += $(TK_CFLAGS)
+
+DEFINES += -DFORCE_PR_LOG
new file mode 100644
--- /dev/null
+++ b/dom/plugins/NPEventOSX.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_plugins_NPEventOSX_h
+#define mozilla_dom_plugins_NPEventOSX_h 1
+
+
+#include "npapi.h"
+#include "IPC/IPCMessageUtils.h"
+
+#warning This is only a stub implementation IMPLEMENT ME
+
+namespace mozilla {
+namespace plugins {
+struct NPRemoteEvent {
+ NPEvent event;
+};
+}
+}
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent>
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ }
+
+ static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+ {
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ }
+};
+
+} // namespace IPC
+
+#endif // ifndef mozilla_dom_plugins_NPEventOSX_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/NPEventWindows.h
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_plugins_NPEventWindows_h
+#define mozilla_dom_plugins_NPEventWindows_h 1
+
+
+#include "npapi.h"
+namespace mozilla {
+
+namespace plugins {
+
+// We use an NPRemoteEvent struct so that we can store the extra data on
+// the stack so that we don't need to worry about managing the memory.
+struct NPRemoteEvent
+{
+ NPEvent event;
+ union {
+ RECT rect;
+ WINDOWPOS windowpos;
+ } lParamData;
+};
+
+}
+
+}
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent>
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ // Make a non-const copy of aParam so that we can muck with
+ // its insides for tranport
+ paramType paramCopy;
+
+ paramCopy.event = aParam.event;
+
+ // We can't blindly ipc events because they may sometimes contain
+ // pointers to memory in the sending process. For example, the
+ // WM_IME_CONTROL with the IMC_GETCOMPOSITIONFONT message has lParam
+ // set to a pointer to a LOGFONT structure.
+ switch (paramCopy.event.event) {
+ case WM_WINDOWPOSCHANGED:
+ // The lParam paramter of WM_WINDOWPOSCHANGED holds a pointer to
+ // a WINDOWPOS structure that contains information about the
+ // window's new size and position
+ paramCopy.lParamData.windowpos = *(reinterpret_cast<WINDOWPOS*>(paramCopy.event.lParam));
+ break;
+ case WM_PAINT:
+ // The lParam paramter of WM_PAINT holds a pointer to an RECT
+ // structure specifying the bounding box of the update area.
+ paramCopy.lParamData.rect = *(reinterpret_cast<RECT*>(paramCopy.event.lParam));
+ break;
+
+ // the white list of events that we will ipc to the client
+ case WM_CHAR:
+ case WM_SYSCHAR:
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ case WM_CONTEXTMENU:
+
+ case WM_CUT:
+ case WM_COPY:
+ case WM_PASTE:
+ case WM_CLEAR:
+ case WM_UNDO:
+
+ case WM_MOUSELEAVE:
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ break;
+
+ default:
+ // RegisterWindowMessage events should be passed.
+ if (paramCopy.event.event >= 0xC000 && paramCopy.event.event <= 0xFFFF)
+ break;
+
+ // ignore any events we don't expect
+ return;
+ }
+
+ aMsg->WriteBytes(¶mCopy, sizeof(paramType));
+ }
+
+ static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+ {
+ const char* bytes = 0;
+
+ if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
+ return false;
+ }
+ memcpy(aResult, bytes, sizeof(paramType));
+
+ if (aResult->event.event == WM_PAINT) {
+ // restore the lParam to point at the RECT
+ aResult->event.lParam = reinterpret_cast<LPARAM>(&aResult->lParamData.rect);
+ } else if (aResult->event.event == WM_WINDOWPOSCHANGED) {
+ // restore the lParam to point at the WINDOWPOS
+ aResult->event.lParam = reinterpret_cast<LPARAM>(&aResult->lParamData.windowpos);
+ }
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(L"(WINEvent)");
+ }
+
+};
+
+} // namespace IPC
+
+#endif // ifndef mozilla_dom_plugins_NPEventWindows_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/NPEventX11.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Jones <jones.chris.g@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_plugins_NPEventX11_h
+#define mozilla_dom_plugins_NPEventX11_h 1
+
+#if defined(MOZ_WIDGET_GTK2)
+# include <gdk/gdkx.h>
+#elif defined(MOZ_WIDGET_QT)
+// X11/X.h has #define CursorShape 0, but Qt's qnamespace.h defines
+// enum CursorShape { ... }. Good times!
+# undef CursorShape
+# include <QX11Info>
+#else
+# error Implement me for your toolkit
+#endif
+
+#include "npapi.h"
+
+namespace mozilla {
+
+namespace plugins {
+
+struct NPRemoteEvent {
+ NPEvent event;
+};
+
+}
+
+}
+
+
+//
+// XEvent is defined as a union of all more specific X*Events.
+// Luckily, as of xorg 1.6.0 / X protocol 11 rev 0, the only pointer
+// field contained in any of these specific X*Event structs is a
+// |Display*|. So to simplify serializing these XEvents, we make the
+//
+// ********** XXX ASSUMPTION XXX **********
+//
+// that the process to which the event is forwarded shares the same
+// display as the process on which the event originated.
+//
+// With this simplification, serialization becomes a simple memcpy to
+// the output stream. Deserialization starts as just a memcpy from
+// the input stream, BUT we then have to write the correct |Display*|
+// into the right field of each X*Event that contains one.
+//
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent> // synonym for XEvent
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteBytes(&aParam, sizeof(paramType));
+ }
+
+ static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+ {
+ const char* bytes = 0;
+
+ if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
+ return false;
+ }
+
+ memcpy(aResult, bytes, sizeof(paramType));
+ SetXDisplay(aResult->event);
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ // TODO
+ aLog->append(L"(XEvent)");
+ }
+
+private:
+ static Display* GetXDisplay(const XAnyEvent& ev)
+ {
+ // TODO: get Display* from Window in |ev|
+
+ // FIXME: do this using Xlib
+#if defined(MOZ_WIDGET_GTK2)
+ return GDK_DISPLAY();
+#elif defined(MOZ_WIDGET_QT)
+ return QX11Info::display();
+#endif
+ }
+
+ static Display* GetXDisplay(const XErrorEvent& ev)
+ {
+ // TODO: get Display* from Window in |ev|
+
+ // FIXME: do this using Xlib
+#if defined(MOZ_WIDGET_GTK2)
+ return GDK_DISPLAY();
+#elif defined(MOZ_WIDGET_QT)
+ return QX11Info::display();
+#endif
+ }
+
+ static void SetXDisplay(XEvent& ev)
+ {
+ if (ev.type >= KeyPress) {
+ ev.xany.display = GetXDisplay(ev.xany);
+ }
+ else {
+ // XXX assuming that this is an error event
+ // (type == 0? not clear from Xlib.h)
+ ev.xerror.display = GetXDisplay(ev.xerror);
+ }
+ }
+};
+
+} // namespace IPC
+
+
+#endif // ifndef mozilla_dom_plugins_NPEventX11_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PBrowserStream.ipdl
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Benjamin Smedberg <benjamin@smedbergs.us>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PPluginInstance.ipdl";
+
+include "mozilla/plugins/PluginMessageUtils.h";
+
+using mozilla::plugins::Buffer;
+using mozilla::plugins::IPCByteRanges;
+
+using NPError;
+using NPReason;
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * NPBrowserStream represents a NPStream sent from the browser to the plugin.
+ */
+
+rpc protocol PBrowserStream
+{
+ manager PPluginInstance;
+
+child:
+ async Write(int32_t offset, Buffer data,
+ uint32_t newlength);
+ rpc NPP_StreamAsFile(nsCString fname);
+
+ /**
+ * NPP_DestroyStream may race with other messages: the child acknowledges
+ * the message with StreamDestroyed before this actor is deleted.
+ */
+ async NPP_DestroyStream(NPReason reason);
+ async __delete__();
+
+parent:
+ rpc NPN_RequestRead(IPCByteRanges ranges)
+ returns (NPError result);
+ async NPN_DestroyStream(NPReason reason);
+ async StreamDestroyed();
+
+/*
+ TODO: turn on state machine.
+
+ // need configurable start state: if the constructor
+ // returns an error in result, start state should
+ // be DELETING.
+start state ALIVE:
+ send Write goto ALIVE;
+ call NPP_StreamAsFile goto ALIVE;
+ send NPP_DestroyStream goto ALIVE;
+ answer NPN_RequestRead goto ALIVE;
+ recv NPN_DestroyStream goto DYING;
+
+state DYING:
+ answer NPN_RequestRead goto DYING;
+ recv NPN_DestroyStream goto DYING;
+ recv StreamDestroyed goto DELETING;
+
+state DELETING:
+ send __delete__;
+*/
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PPluginIdentifier.ipdl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PPluginModule.ipdl";
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * Represents an NPIdentifier that wraps either a string or an integer.
+ */
+async protocol PPluginIdentifier
+{
+ manager PPluginModule;
+
+child:
+ async __delete__();
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PPluginInstance.ipdl
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Benjamin Smedberg <benjamin@smedbergs.us>
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PPluginModule.ipdl";
+include protocol "PPluginScriptableObject.ipdl";
+include protocol "PBrowserStream.ipdl";
+include protocol "PPluginStream.ipdl";
+include protocol "PStreamNotify.ipdl";
+
+include "mozilla/plugins/PluginMessageUtils.h";
+
+using NPError;
+using NPRemoteWindow;
+using NPRemoteEvent;
+using NPRect;
+using NPNURLVariable;
+using mozilla::plugins::NativeWindowHandle;
+
+namespace mozilla {
+namespace plugins {
+
+rpc protocol PPluginInstance
+{
+ manager PPluginModule;
+
+ manages PPluginScriptableObject;
+ manages PBrowserStream;
+ manages PPluginStream;
+ manages PStreamNotify;
+
+child:
+ rpc __delete__();
+
+ rpc NPP_SetWindow(NPRemoteWindow window);
+
+ // this message is not used on non-X platforms
+ rpc NPP_GetValue_NPPVpluginNeedsXEmbed()
+ returns (bool value, NPError result);
+ rpc NPP_GetValue_NPPVpluginScriptableNPObject()
+ returns (nullable PPluginScriptableObject value, NPError result);
+
+ rpc NPP_SetValue_NPNVprivateModeBool(bool value) returns (NPError result);
+
+ rpc NPP_HandleEvent(NPRemoteEvent event)
+ returns (int16_t handled);
+ // special cases of HandleEvent to make mediating races simpler
+ rpc Paint(NPRemoteEvent event)
+ returns (int16_t handled);
+ // this is only used on windows to forward WM_WINDOWPOSCHANGE
+ async WindowPosChanged(NPRemoteEvent event);
+
+ rpc NPP_Destroy()
+ returns (NPError rv);
+
+parent:
+ rpc NPN_GetValue_NPNVjavascriptEnabledBool()
+ returns (bool value, NPError result);
+ rpc NPN_GetValue_NPNVisOfflineBool()
+ returns (bool value, NPError result);
+ rpc NPN_GetValue_NPNVWindowNPObject()
+ returns (nullable PPluginScriptableObject value, NPError result);
+ rpc NPN_GetValue_NPNVPluginElementNPObject()
+ returns (nullable PPluginScriptableObject value, NPError result);
+ rpc NPN_GetValue_NPNVprivateModeBool()
+ returns (bool value, NPError result);
+ rpc NPN_GetValue_NPNVnetscapeWindow()
+ returns (NativeWindowHandle value, NPError result);
+
+ rpc NPN_SetValue_NPPVpluginWindow(bool windowed)
+ returns (NPError result);
+ rpc NPN_SetValue_NPPVpluginTransparent(bool transparent)
+ returns (NPError result);
+
+ rpc NPN_GetURL(nsCString url, nsCString target)
+ returns (NPError result);
+ rpc NPN_PostURL(nsCString url, nsCString target, nsCString buffer, bool file)
+ returns (NPError result);
+
+ /**
+ * Covers both NPN_GetURLNotify and NPN_PostURLNotify.
+ * @TODO This would be more readable as an overloaded method,
+ * but IPDL doesn't allow that for constructors.
+ */
+ rpc PStreamNotify(nsCString url, nsCString target, bool post,
+ nsCString buffer, bool file)
+ returns (NPError result);
+
+ async NPN_InvalidateRect(NPRect rect);
+
+ rpc NPN_PushPopupsEnabledState(bool aState)
+ returns (bool aSuccess);
+
+ rpc NPN_PopPopupsEnabledState()
+ returns (bool aSuccess);
+
+ rpc NPN_GetValueForURL(NPNURLVariable variable, nsCString url)
+ returns (nsCString value, NPError result);
+
+ rpc NPN_SetValueForURL(NPNURLVariable variable, nsCString url,
+ nsCString value)
+ returns (NPError result);
+
+ rpc NPN_GetAuthenticationInfo(nsCString protocol_, nsCString host,
+ int32_t port, nsCString scheme,
+ nsCString realm)
+ returns (nsCString username, nsCString password, NPError result);
+
+both:
+ async PPluginScriptableObject();
+
+child:
+ /* NPP_NewStream */
+ rpc PBrowserStream(nsCString url,
+ uint32_t length,
+ uint32_t lastmodified,
+ nullable PStreamNotify notifyData,
+ nsCString headers,
+ nsCString mimeType,
+ bool seekable)
+ returns (NPError rv,
+ uint16_t stype);
+
+parent:
+ /* NPN_NewStream */
+ rpc PPluginStream(nsCString mimeType,
+ nsCString target)
+ returns (NPError result);
+
+parent:
+ rpc PluginGotFocus();
+ sync SetNestedEventState(bool aState);
+child:
+ rpc SetPluginFocus();
+ rpc UpdateWindow();
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PPluginModule.ipdl
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Benjamin Smedberg <benjamin@smedbergs.us>
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PPluginIdentifier.ipdl";
+include protocol "PPluginInstance.ipdl";
+
+include "npapi.h";
+include "mozilla/plugins/PluginMessageUtils.h";
+
+using NPError;
+using NPNVariable;
+using mozilla::plugins::NativeThreadId;
+
+namespace mozilla {
+namespace plugins {
+
+rpc protocol PPluginModule
+{
+ manages PPluginInstance;
+ manages PPluginIdentifier;
+
+both:
+ /**
+ * Sending a void string to this constructor creates an int identifier whereas
+ * sending a non-void string will create a string identifier. This constructor
+ * may be called by either child or parent. If a race occurs by calling the
+ * constructor with the same string or int argument then we create two actors
+ * and detect the second instance in the child. We prevent the parent's actor
+ * from leaking out to plugin code and only allow the child's to be used.
+ */
+ async PPluginIdentifier(nsCString aString,
+ int32_t aInt);
+
+child:
+ // Return the plugin's thread ID, if it can be found.
+ rpc NP_Initialize()
+ returns (NativeThreadId tid, NPError rv);
+
+ rpc PPluginInstance(nsCString aMimeType,
+ uint16_t aMode,
+ nsCString[] aNames,
+ nsCString[] aValues)
+ returns (NPError rv);
+
+ rpc NP_Shutdown()
+ returns (NPError rv);
+
+parent:
+ rpc NPN_UserAgent()
+ returns (nsCString userAgent);
+
+ rpc NPN_GetValue_WithBoolReturn(NPNVariable aVariable)
+ returns (NPError aError,
+ bool aBoolVal);
+
+ // Wake up and process a few native events. Periodically called by
+ // Gtk-specific code upon detecting that the plugin process has
+ // entered a nested event loop. If the browser doesn't process
+ // native events, then "livelock" and some other glitches can occur.
+ rpc ProcessSomeEvents();
+
+ sync AppendNotesToCrashReport(nsCString aNotes);
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PPluginScriptableObject.ipdl
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Benjamin Smedberg <benjamin@smedbergs.us>
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PPluginInstance.ipdl";
+include protocol "PPluginIdentifier.ipdl";
+
+include "npapi.h";
+include "npruntime.h";
+include "mozilla/plugins/PluginMessageUtils.h";
+
+using mozilla::void_t;
+using mozilla::null_t;
+
+namespace mozilla {
+namespace plugins {
+
+union Variant {
+ void_t;
+ null_t;
+ bool;
+ int;
+ double;
+ nsCString;
+ nullable PPluginScriptableObject;
+};
+
+rpc protocol PPluginScriptableObject
+{
+ manager PPluginInstance;
+
+both:
+ async __delete__();
+
+parent:
+ rpc NPN_Evaluate(nsCString aScript)
+ returns (Variant aResult,
+ bool aSuccess);
+
+child:
+ rpc Invalidate();
+
+both:
+ // NPClass methods
+ rpc HasMethod(PPluginIdentifier aId)
+ returns (bool aHasMethod);
+
+ rpc Invoke(PPluginIdentifier aId,
+ Variant[] aArgs)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ rpc InvokeDefault(Variant[] aArgs)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ rpc HasProperty(PPluginIdentifier aId)
+ returns (bool aHasProperty);
+
+ rpc GetProperty(PPluginIdentifier aId)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ rpc SetProperty(PPluginIdentifier aId,
+ Variant aValue)
+ returns (bool aSuccess);
+
+ rpc RemoveProperty(PPluginIdentifier aId)
+ returns (bool aSuccess);
+
+ rpc Enumerate()
+ returns (PPluginIdentifier[] aProperties,
+ bool aSuccess);
+
+ rpc Construct(Variant[] aArgs)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ // Objects are initially unprotected, and the Protect and Unprotect functions
+ // only affect protocol objects that represent NPObjects created in the same
+ // process (rather than protocol objects that are a proxy for an NPObject
+ // created in another process). Protocol objects representing local NPObjects
+ // are protected after an NPObject has been associated with the protocol
+ // object. Sending the protocol object as an argument to the other process
+ // temporarily protects the protocol object again for the duration of the call.
+ async Protect();
+ async Unprotect();
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PPluginStream.ipdl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation <http://www.mozilla.org/>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PPluginInstance.ipdl";
+
+include "mozilla/plugins/PluginMessageUtils.h";
+
+using mozilla::plugins::Buffer;
+using NPError;
+using NPReason;
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * PPluginStream represents an NPStream sent from the plugin to the browser.
+ */
+
+rpc protocol PPluginStream
+{
+ manager PPluginInstance;
+
+parent:
+ rpc NPN_Write(Buffer data) returns (int32_t written);
+
+both:
+ /**
+ * ~PPluginStream is for both NPN_DestroyStream and NPP_DestroyStream.
+ * @param artificial True when the stream is closed as a by-product of
+ * some other call (such as a failure in NPN_Write).
+ */
+ rpc __delete__(NPReason reason, bool artificial);
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PStreamNotify.ipdl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+
+include protocol "PPluginInstance.ipdl";
+
+include "npapi.h";
+
+using NPReason;
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This empty protocol exists only to be constructed and destroyed.
+ */
+rpc protocol PStreamNotify
+{
+ manager PPluginInstance;
+
+child:
+ /**
+ * Represents NPP_URLNotify
+ */
+ async __delete__(NPReason reason);
+};
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginIdentifierChild.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef dom_plugins_PluginIdentifierChild_h
+#define dom_plugins_PluginIdentifierChild_h
+
+#include "mozilla/plugins/PPluginIdentifierChild.h"
+#include "mozilla/plugins/PluginModuleChild.h"
+
+#include "nsStringGlue.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginIdentifierChild : public PPluginIdentifierChild
+{
+ friend class PluginModuleChild;
+public:
+ bool IsString()
+ {
+ return reinterpret_cast<intptr_t>(mCanonicalIdentifier) & 1;
+ }
+
+ NPIdentifier ToNPIdentifier()
+ {
+ return reinterpret_cast<PluginIdentifierChild*>(
+ reinterpret_cast<intptr_t>(mCanonicalIdentifier) & ~1);
+ }
+
+protected:
+ PluginIdentifierChild(bool aIsString)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(mCanonicalIdentifier(this))
+ {
+ MOZ_COUNT_CTOR(PluginIdentifierChild);
+ if (aIsString) {
+ SetIsString();
+ }
+ }
+
+ virtual ~PluginIdentifierChild()
+ {
+ MOZ_COUNT_DTOR(PluginIdentifierChild);
+ }
+
+ void SetCanonicalIdentifier(PluginIdentifierChild* aIdentifier)
+ {
+ NS_ASSERTION(ToNPIdentifier() == this, "Already got one!");
+ bool isString = IsString();
+ mCanonicalIdentifier = aIdentifier;
+ if (isString) {
+ SetIsString();
+ }
+ }
+
+private:
+ void SetIsString()
+ {
+ mCanonicalIdentifier = reinterpret_cast<PluginIdentifierChild*>(
+ reinterpret_cast<intptr_t>(mCanonicalIdentifier) | 1);
+ }
+
+ PluginIdentifierChild* mCanonicalIdentifier;
+};
+
+class PluginIdentifierChildString : public PluginIdentifierChild
+{
+ friend class PluginModuleChild;
+public:
+ NPUTF8* ToString()
+ {
+ return ToNewCString(mString);
+ }
+
+protected:
+ PluginIdentifierChildString(const nsCString& aString)
+ : PluginIdentifierChild(true),
+ mString(aString)
+ { }
+
+ nsCString mString;
+};
+
+class PluginIdentifierChildInt : public PluginIdentifierChild
+{
+ friend class PluginModuleChild;
+public:
+ int32_t ToInt()
+ {
+ return mInt;
+ }
+
+protected:
+ PluginIdentifierChildInt(int32_t aInt)
+ : PluginIdentifierChild(false),
+ mInt(aInt)
+ { }
+
+ int32_t mInt;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_PluginIdentifierChild_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginIdentifierParent.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugins.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef dom_plugins_PluginIdentifierParent_h
+#define dom_plugins_PluginIdentifierParent_h
+
+#include "mozilla/plugins/PPluginIdentifierParent.h"
+
+#include "npapi.h"
+#include "npruntime.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginIdentifierParent : public PPluginIdentifierParent
+{
+ friend class PluginModuleParent;
+
+public:
+ NPIdentifier ToNPIdentifier()
+ {
+ return mIdentifier;
+ }
+
+protected:
+ PluginIdentifierParent(NPIdentifier aIdentifier)
+ : mIdentifier(aIdentifier)
+ {
+ MOZ_COUNT_CTOR(PluginIdentifierParent);
+ }
+
+ virtual ~PluginIdentifierParent()
+ {
+ MOZ_COUNT_DTOR(PluginIdentifierParent);
+ }
+
+private:
+ NPIdentifier mIdentifier;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_PluginIdentifierParent_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -0,0 +1,1656 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jim Mathies <jmathies@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "PluginInstanceChild.h"
+#include "PluginModuleChild.h"
+#include "BrowserStreamChild.h"
+#include "PluginStreamChild.h"
+#include "StreamNotifyChild.h"
+
+#include "mozilla/ipc/SyncChannel.h"
+
+using namespace mozilla::plugins;
+
+#ifdef MOZ_WIDGET_GTK2
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include "gtk2xtbin.h"
+
+#elif defined(MOZ_WIDGET_QT)
+#include <QX11Info>
+#elif defined(OS_WIN)
+
+#include "nsWindowsDllInterceptor.h"
+
+typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
+ UINT uFlags,
+ int x,
+ int y,
+ int nReserved,
+ HWND hWnd,
+ CONST RECT *prcRect);
+static WindowsDllInterceptor sUser32Intercept;
+static HWND sWinlessPopupSurrogateHWND = NULL;
+static User32TrackPopupMenu sUser32TrackPopupMenuStub = NULL;
+
+using mozilla::gfx::SharedDIB;
+
+#include <windows.h>
+#include <windowsx.h>
+
+#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
+
+#endif // defined(OS_WIN)
+
+PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+ const nsCString& aMimeType)
+ : mPluginIface(aPluginIface)
+ , mQuirks(0)
+ , mCachedWindowActor(nsnull)
+ , mCachedElementActor(nsnull)
+#if defined(OS_WIN)
+ , mPluginWindowHWND(0)
+ , mPluginWndProc(0)
+ , mPluginParentHWND(0)
+ , mNestedEventHook(0)
+ , mNestedEventLevelDepth(0)
+ , mNestedEventState(false)
+ , mCachedWinlessPluginHWND(0)
+ , mWinlessPopupSurrogateHWND(0)
+#endif // OS_WIN
+{
+ memset(&mWindow, 0, sizeof(mWindow));
+ mData.ndata = (void*) this;
+ mData.pdata = nsnull;
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ mWindow.ws_info = &mWsInfo;
+ memset(&mWsInfo, 0, sizeof(mWsInfo));
+#ifdef MOZ_WIDGET_GTK2
+ mWsInfo.display = GDK_DISPLAY();
+#elif defined(MOZ_WIDGET_QT)
+ mWsInfo.display = QX11Info::display();
+#endif // MOZ_WIDGET_GTK2
+#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
+#if defined(OS_WIN)
+ memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
+ mAlphaExtract.doublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
+#endif // OS_WIN
+ InitQuirksModes(aMimeType);
+#if defined(OS_WIN)
+ InitPopupMenuHook();
+#endif // OS_WIN
+}
+
+PluginInstanceChild::~PluginInstanceChild()
+{
+#if defined(OS_WIN)
+ DestroyPluginWindow();
+#endif
+}
+
+void
+PluginInstanceChild::InitQuirksModes(const nsCString& aMimeType)
+{
+#ifdef OS_WIN
+ // application/x-silverlight
+ // application/x-silverlight-2
+ NS_NAMED_LITERAL_CSTRING(silverlight, "application/x-silverlight");
+ // application/x-shockwave-flash
+ NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash");
+ if (FindInReadable(silverlight, aMimeType)) {
+ mQuirks |= QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION;
+ mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
+ }
+ else if (FindInReadable(flash, aMimeType)) {
+ mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
+ }
+#endif
+}
+
+NPError
+PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
+ NPObject** aObject)
+{
+ PluginScriptableObjectChild* actor;
+ NPError result = NPERR_NO_ERROR;
+
+ switch (aValue) {
+ case NPNVWindowNPObject:
+ if (!(actor = mCachedWindowActor)) {
+ PPluginScriptableObjectChild* actorProtocol;
+ CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result);
+ if (result == NPERR_NO_ERROR) {
+ actor = mCachedWindowActor =
+ static_cast<PluginScriptableObjectChild*>(actorProtocol);
+ NS_ASSERTION(actor, "Null actor!");
+ PluginModuleChild::sBrowserFuncs.retainobject(
+ actor->GetObject(false));
+ }
+ }
+ break;
+
+ case NPNVPluginElementNPObject:
+ if (!(actor = mCachedElementActor)) {
+ PPluginScriptableObjectChild* actorProtocol;
+ CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol,
+ &result);
+ if (result == NPERR_NO_ERROR) {
+ actor = mCachedElementActor =
+ static_cast<PluginScriptableObjectChild*>(actorProtocol);
+ NS_ASSERTION(actor, "Null actor!");
+ PluginModuleChild::sBrowserFuncs.retainobject(
+ actor->GetObject(false));
+ }
+ }
+ break;
+
+ default:
+ NS_NOTREACHED("Don't know what to do with this value type!");
+ }
+
+#ifdef DEBUG
+ {
+ NPError currentResult;
+ PPluginScriptableObjectChild* currentActor;
+
+ switch (aValue) {
+ case NPNVWindowNPObject:
+ CallNPN_GetValue_NPNVWindowNPObject(¤tActor,
+ ¤tResult);
+ break;
+ case NPNVPluginElementNPObject:
+ CallNPN_GetValue_NPNVPluginElementNPObject(¤tActor,
+ ¤tResult);
+ break;
+ default:
+ NS_NOTREACHED("Don't know what to do with this value type!");
+ }
+
+ // Make sure that the current actor returned by the parent matches our
+ // cached actor!
+ NS_ASSERTION(static_cast<PluginScriptableObjectChild*>(currentActor) ==
+ actor, "Cached actor is out of date!");
+ NS_ASSERTION(currentResult == result, "Results don't match?!");
+ }
+#endif
+
+ if (result != NPERR_NO_ERROR) {
+ return result;
+ }
+
+ NPObject* object = actor->GetObject(false);
+ NS_ASSERTION(object, "Null object?!");
+
+ *aObject = PluginModuleChild::sBrowserFuncs.retainobject(object);
+ return NPERR_NO_ERROR;
+
+}
+
+NPError
+PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
+ void* aValue)
+{
+ PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
+ AssertPluginThread();
+
+ switch(aVar) {
+
+ case NPNVSupportsWindowless:
+#if defined(OS_LINUX) || defined(OS_WIN)
+ *((NPBool*)aValue) = true;
+#else
+ *((NPBool*)aValue) = false;
+#endif
+ return NPERR_NO_ERROR;
+
+#if defined(OS_LINUX)
+ case NPNVSupportsXEmbedBool:
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+
+ case NPNVToolkit:
+ *((NPNToolkitType*)aValue) = NPNVGtk2;
+ return NPERR_NO_ERROR;
+
+#elif defined(OS_WIN)
+ case NPNVToolkit:
+ return NPERR_GENERIC_ERROR;
+#endif
+ case NPNVjavascriptEnabledBool: {
+ bool v = false;
+ NPError result;
+ if (!CallNPN_GetValue_NPNVjavascriptEnabledBool(&v, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *static_cast<NPBool*>(aValue) = v;
+ return result;
+ }
+
+ case NPNVisOfflineBool: {
+ bool v = false;
+ NPError result;
+ if (!CallNPN_GetValue_NPNVisOfflineBool(&v, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *static_cast<NPBool*>(aValue) = v;
+ return result;
+ }
+
+ case NPNVprivateModeBool: {
+ bool v = false;
+ NPError result;
+ if (!CallNPN_GetValue_NPNVprivateModeBool(&v, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *static_cast<NPBool*>(aValue) = v;
+ return result;
+ }
+
+ case NPNVWindowNPObject: // Intentional fall-through
+ case NPNVPluginElementNPObject: {
+ NPObject* object;
+ NPError result = InternalGetNPObjectForValue(aVar, &object);
+ if (result == NPERR_NO_ERROR) {
+ *((NPObject**)aValue) = object;
+ }
+ return result;
+ }
+
+ case NPNVnetscapeWindow: {
+#ifdef XP_WIN
+ if (mWindow.type == NPWindowTypeDrawable) {
+ if (mCachedWinlessPluginHWND) {
+ *static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
+ return NPERR_NO_ERROR;
+ }
+ NPError result;
+ if (!CallNPN_GetValue_NPNVnetscapeWindow(&mCachedWinlessPluginHWND, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
+ return result;
+ }
+ else {
+ *static_cast<HWND*>(aValue) = mPluginWindowHWND;
+ return NPERR_NO_ERROR;
+ }
+#elif defined(MOZ_X11)
+ NPError result;
+ CallNPN_GetValue_NPNVnetscapeWindow(static_cast<XID*>(aValue), &result);
+ return result;
+#else
+ return NPERR_GENERIC_ERROR;
+#endif
+ }
+
+ default:
+ PR_LOG(gPluginLog, PR_LOG_WARNING,
+ ("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i (%s)",
+ (int) aVar, NPNVariableToString(aVar)));
+ return NPERR_GENERIC_ERROR;
+ }
+
+}
+
+
+NPError
+PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
+{
+ PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s (aVar=%i, aValue=%p)",
+ FULLFUNCTION, (int) aVar, aValue));
+
+ AssertPluginThread();
+
+ switch (aVar) {
+ case NPPVpluginWindowBool: {
+ NPError rv;
+ bool windowed = (NPBool) (intptr_t) aValue;
+
+ if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
+ return NPERR_GENERIC_ERROR;
+
+ return rv;
+ }
+
+ case NPPVpluginTransparentBool: {
+ NPError rv;
+ bool transparent = (NPBool) (intptr_t) aValue;
+
+ if (!CallNPN_SetValue_NPPVpluginTransparent(transparent, &rv))
+ return NPERR_GENERIC_ERROR;
+
+ return rv;
+ }
+
+ default:
+ PR_LOG(gPluginLog, PR_LOG_WARNING,
+ ("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
+ (int) aVar, NPPVariableToString(aVar)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+bool
+PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(
+ bool* needs, NPError* rv)
+{
+ AssertPluginThread();
+
+#ifdef MOZ_X11
+ // The documentation on the types for many variables in NP(N|P)_GetValue
+ // is vague. Often boolean values are NPBool (1 byte), but
+ // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
+ // treats NPPVpluginNeedsXEmbed as PRBool (int), and
+ // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
+ // thus we can't use NPBool for needsXEmbed, or the three bytes above
+ // it on the stack would get clobbered. so protect with the larger PRBool.
+ PRBool needsXEmbed = 0;
+ if (!mPluginIface->getvalue) {
+ *rv = NPERR_GENERIC_ERROR;
+ }
+ else {
+ *rv = mPluginIface->getvalue(GetNPP(), NPPVpluginNeedsXEmbed,
+ &needsXEmbed);
+ }
+ *needs = needsXEmbed;
+ return true;
+
+#else
+
+ NS_RUNTIMEABORT("shouldn't be called on non-X11 platforms");
+ return false; // not reached
+
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
+ PPluginScriptableObjectChild** aValue,
+ NPError* aResult)
+{
+ AssertPluginThread();
+
+ NPObject* object = nsnull;
+ NPError result = NPERR_GENERIC_ERROR;
+ if (mPluginIface->getvalue) {
+ result = mPluginIface->getvalue(GetNPP(), NPPVpluginScriptableNPObject,
+ &object);
+ }
+ if (result == NPERR_NO_ERROR && object) {
+ PluginScriptableObjectChild* actor = GetActorForNPObject(object);
+
+ // If we get an actor then it has retained. Otherwise we don't need it
+ // any longer.
+ PluginModuleChild::sBrowserFuncs.releaseobject(object);
+ if (actor) {
+ *aValue = actor;
+ *aResult = NPERR_NO_ERROR;
+ return true;
+ }
+
+ NS_ERROR("Failed to get actor!");
+ result = NPERR_GENERIC_ERROR;
+ }
+ else {
+ result = NPERR_GENERIC_ERROR;
+ }
+
+ *aValue = nsnull;
+ *aResult = result;
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
+ NPError* result)
+{
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+ }
+
+ // Use `long` instead of NPBool because Flash and other plugins read
+ // this as a word-size value instead of the 1-byte NPBool that it is.
+ long v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
+ int16_t* handled)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+#if defined(OS_LINUX) && defined(DEBUG)
+ if (GraphicsExpose == event.event.type)
+ PLUGIN_LOG_DEBUG((" received drawable 0x%lx\n",
+ event.event.xgraphicsexpose.drawable));
+#endif
+
+ // Make a copy since we may modify values.
+ NPEvent evcopy = event.event;
+
+#ifdef OS_WIN
+ // Painting for win32. SharedSurfacePaint handles everything.
+ if (mWindow.type == NPWindowTypeDrawable) {
+ if (evcopy.event == WM_PAINT) {
+ *handled = SharedSurfacePaint(evcopy);
+ return true;
+ }
+ else if (evcopy.event == mAlphaExtract.doublePassEvent) {
+ // We'll render to mSharedSurfaceDib first, then render to a cached bitmap
+ // we store locally. The two passes are for alpha extraction, so the second
+ // pass must be to a flat white surface in order for things to work.
+ mAlphaExtract.doublePass = RENDER_BACK_ONE;
+ *handled = true;
+ return true;
+ }
+ }
+ *handled = WinlessHandleEvent(evcopy);
+ return true;
+#endif
+
+ if (!mPluginIface->event)
+ *handled = false;
+ else
+ *handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
+
+#ifdef MOZ_X11
+ if (GraphicsExpose == event.event.type) {
+ // Make sure the X server completes the drawing before the parent
+ // draws on top and destroys the Drawable.
+ //
+ // XSync() waits for the X server to complete. Really this child
+ // process does not need to wait; the parent is the process that needs
+ // to wait. A possibly-slightly-better alternative would be to send
+ // an X event to the parent that the parent would wait for.
+ XSync(mWsInfo.display, False);
+ }
+#endif
+
+ return true;
+}
+
+bool
+PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
+{
+#ifdef OS_WIN
+ int16_t dontcare;
+ return AnswerNPP_HandleEvent(event, &dontcare);
+#else
+ NS_RUNTIMEABORT("WindowPosChanged is a windows-only message");
+ return false;
+#endif
+}
+
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+static bool
+XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
+ Visual** aVisual, unsigned int* aDepth)
+{
+ if (aVisualID == None) {
+ *aVisual = NULL;
+ *aDepth = 0;
+ return true;
+ }
+
+ const Screen* screen = DefaultScreenOfDisplay(aDisplay);
+
+ for (int d = 0; d < screen->ndepths; d++) {
+ Depth *d_info = &screen->depths[d];
+ for (int v = 0; v < d_info->nvisuals; v++) {
+ Visual* visual = &d_info->visuals[v];
+ if (visual->visualid == aVisualID) {
+ *aVisual = visual;
+ *aDepth = d_info->depth;
+ return true;
+ }
+ }
+ }
+
+ NS_ERROR("VisualID not on Screen.");
+ return false;
+}
+#endif
+
+bool
+PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
+{
+ PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
+ FULLFUNCTION,
+ aWindow.window,
+ aWindow.x, aWindow.y,
+ aWindow.width, aWindow.height));
+ AssertPluginThread();
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ // The minimum info is sent over IPC to allow this
+ // code to determine the rest.
+
+ mWindow.window = reinterpret_cast<void*>(aWindow.window);
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.clipRect = aWindow.clipRect;
+ mWindow.type = aWindow.type;
+
+ mWsInfo.colormap = aWindow.colormap;
+ if (!XVisualIDToInfo(mWsInfo.display, aWindow.visualID,
+ &mWsInfo.visual, &mWsInfo.depth))
+ return false;
+
+ if (aWindow.type == NPWindowTypeWindow) {
+#ifdef MOZ_WIDGET_GTK2
+ if (GdkWindow* socket_window = gdk_window_lookup(aWindow.window)) {
+ // A GdkWindow for the socket already exists. Need to
+ // workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061
+ // See wrap_gtk_plug_embedded in PluginModuleChild.cpp.
+ g_object_set_data(G_OBJECT(socket_window),
+ "moz-existed-before-set-window",
+ GUINT_TO_POINTER(1));
+ }
+#endif
+ }
+
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+
+#elif defined(OS_WIN)
+ switch (aWindow.type) {
+ case NPWindowTypeWindow:
+ {
+ if (!CreatePluginWindow())
+ return false;
+
+ ReparentPluginWindow((HWND)aWindow.window);
+ SizePluginWindow(aWindow.width, aWindow.height);
+
+ mWindow.window = (void*)mPluginWindowHWND;
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.type = aWindow.type;
+
+ if (mPluginIface->setwindow) {
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+ WNDPROC wndProc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
+ if (wndProc != PluginWindowProc) {
+ mPluginWndProc = reinterpret_cast<WNDPROC>(
+ SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(PluginWindowProc)));
+ }
+ }
+ }
+ break;
+
+ case NPWindowTypeDrawable:
+ if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK)
+ CreateWinlessPopupSurrogate();
+ return SharedSurfaceSetWindow(aWindow);
+ break;
+
+ default:
+ NS_NOTREACHED("Bad plugin window type.");
+ return false;
+ break;
+ }
+
+#elif defined(OS_MACOSX)
+# warning This is only a stub implementation IMPLEMENT ME
+
+#else
+# error Implement me for your OS
+#endif
+
+ return true;
+}
+
+bool
+PluginInstanceChild::Initialize()
+{
+ return true;
+}
+
+#if defined(OS_WIN)
+
+static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
+static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty");
+
+// static
+bool
+PluginInstanceChild::RegisterWindowClass()
+{
+ static bool alreadyRegistered = false;
+ if (alreadyRegistered)
+ return true;
+
+ alreadyRegistered = true;
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = DummyWindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kWindowClassName;
+ wcex.hIconSm = 0;
+
+ return RegisterClassEx(&wcex) ? true : false;
+}
+
+bool
+PluginInstanceChild::CreatePluginWindow()
+{
+ // already initialized
+ if (mPluginWindowHWND)
+ return true;
+
+ if (!RegisterWindowClass())
+ return false;
+
+ mPluginWindowHWND =
+ CreateWindowEx(WS_EX_LEFT | WS_EX_LTRREADING |
+ WS_EX_NOPARENTNOTIFY | // XXXbent Get rid of this!
+ WS_EX_RIGHTSCROLLBAR,
+ kWindowClassName, 0,
+ WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0,
+ 0, 0, NULL, 0, GetModuleHandle(NULL), 0);
+ if (!mPluginWindowHWND)
+ return false;
+ if (!SetProp(mPluginWindowHWND, kPluginInstanceChildProperty, this))
+ return false;
+
+ // Apparently some plugins require an ASCII WndProc.
+ SetWindowLongPtrA(mPluginWindowHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(DefWindowProcA));
+
+ return true;
+}
+
+void
+PluginInstanceChild::DestroyPluginWindow()
+{
+ if (mPluginWindowHWND) {
+ // Unsubclass the window.
+ WNDPROC wndProc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
+ if (wndProc == PluginWindowProc) {
+ NS_ASSERTION(mPluginWndProc, "Should have old proc here!");
+ SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(mPluginWndProc));
+ mPluginWndProc = 0;
+ }
+
+ RemoveProp(mPluginWindowHWND, kPluginInstanceChildProperty);
+ DestroyWindow(mPluginWindowHWND);
+ mPluginWindowHWND = 0;
+ }
+}
+
+void
+PluginInstanceChild::ReparentPluginWindow(HWND hWndParent)
+{
+ if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) {
+ // Fix the child window's style to be a child window.
+ LONG style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE);
+ style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+ style &= ~WS_POPUP;
+ SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style);
+
+ // Do the reparenting.
+ SetParent(mPluginWindowHWND, hWndParent);
+
+ // Make sure we're visible.
+ ShowWindow(mPluginWindowHWND, SW_SHOWNA);
+ }
+ mPluginParentHWND = hWndParent;
+}
+
+void
+PluginInstanceChild::SizePluginWindow(int width,
+ int height)
+{
+ if (mPluginWindowHWND) {
+ mPluginSize.x = width;
+ mPluginSize.y = height;
+ SetWindowPos(mPluginWindowHWND, NULL, 0, 0, width, height,
+ SWP_NOZORDER | SWP_NOREPOSITION);
+ }
+}
+
+// See chromium's webplugin_delegate_impl.cc for explanation of this function.
+// static
+LRESULT CALLBACK
+PluginInstanceChild::DummyWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ return CallWindowProc(DefWindowProc, hWnd, message, wParam, lParam);
+}
+
+// static
+LRESULT CALLBACK
+PluginInstanceChild::PluginWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
+ "Failed to prevent a nonqueued message from running!");
+
+ PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
+ GetProp(hWnd, kPluginInstanceChildProperty));
+ if (!self) {
+ NS_NOTREACHED("Badness!");
+ return 0;
+ }
+
+ NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!");
+
+ // Adobe's shockwave positions the plugin window relative to the browser
+ // frame when it initializes. With oopp disabled, this wouldn't have an
+ // effect. With oopp, GeckoPluginWindow is a child of the parent plugin
+ // window, so the move offsets the child within the parent. Generally
+ // we don't want plugins moving or sizing our window, so we prevent these
+ // changes here.
+ if (message == WM_WINDOWPOSCHANGING) {
+ WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
+ if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) {
+ pos->x = pos->y = 0;
+ pos->cx = self->mPluginSize.x;
+ pos->cy = self->mPluginSize.y;
+ LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
+ lParam);
+ pos->x = pos->y = 0;
+ pos->cx = self->mPluginSize.x;
+ pos->cy = self->mPluginSize.y;
+ return res;
+ }
+ }
+
+ // The plugin received keyboard focus, let the parent know so the dom is up to date.
+ if (message == WM_MOUSEACTIVATE)
+ self->CallPluginGotFocus();
+
+ // Prevent lockups due to plugins making rpc calls when the parent
+ // is making a synchronous SetFocus api call. (bug 541362) Add more
+ // windowing events as needed for other api.
+ if (message == WM_KILLFOCUS &&
+ ((InSendMessageEx(NULL) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND)) {
+ ReplyMessage(0); // Unblock the caller
+ }
+
+ LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
+ lParam);
+
+ if (message == WM_CLOSE)
+ self->DestroyPluginWindow();
+
+ if (message == WM_NCDESTROY)
+ RemoveProp(hWnd, kPluginInstanceChildProperty);
+
+ return res;
+}
+
+/* winless modal ui loop logic */
+
+// gTempChildPointer is only in use from the time we enter handle event, to the
+// point where ui might be created by that call. If ui isn't created, there's
+// no issue. If ui is created, the parent can't start processing messages in
+// spin loop until InternalCallSetNestedEventState is set, at which point,
+// gTempChildPointer is no longer needed.
+static PluginInstanceChild* gTempChildPointer;
+
+LRESULT CALLBACK
+PluginInstanceChild::NestedInputEventHook(int nCode,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (!gTempChildPointer) {
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+ }
+
+ if (nCode >= 0) {
+ NS_ASSERTION(gTempChildPointer, "Never should be null here!");
+ gTempChildPointer->ResetNestedEventHook();
+ gTempChildPointer->InternalCallSetNestedEventState(true);
+
+ gTempChildPointer = NULL;
+ }
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+void
+PluginInstanceChild::SetNestedInputEventHook()
+{
+ NS_ASSERTION(!mNestedEventHook,
+ "mNestedEventHook already setup in call to SetNestedInputEventHook?");
+
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ // WH_GETMESSAGE hooks are triggered by peek message calls in parent due to
+ // attached message queues, resulting in stomped in-process ipc calls. So
+ // we use a filter hook specific to dialogs, menus, and scroll bars to kick
+ // things off.
+ mNestedEventHook = SetWindowsHookEx(WH_MSGFILTER,
+ NestedInputEventHook,
+ NULL,
+ GetCurrentThreadId());
+}
+
+void
+PluginInstanceChild::ResetNestedEventHook()
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+ if (mNestedEventHook)
+ UnhookWindowsHookEx(mNestedEventHook);
+ mNestedEventHook = NULL;
+}
+
+void
+PluginInstanceChild::InternalCallSetNestedEventState(bool aState)
+{
+ if (aState != mNestedEventState) {
+ PLUGIN_LOG_DEBUG(
+ ("PluginInstanceChild::InternalCallSetNestedEventState(%i)",
+ (int)aState));
+ mNestedEventState = aState;
+ SendSetNestedEventState(mNestedEventState);
+ }
+}
+
+/* windowless track popup menu helpers */
+
+BOOL
+WINAPI
+PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
+ UINT uFlags,
+ int x,
+ int y,
+ int nReserved,
+ HWND hWnd,
+ CONST RECT *prcRect)
+{
+ if (!sUser32TrackPopupMenuStub) {
+ NS_ERROR("TrackPopupMenu stub isn't set! Badness!");
+ return 0;
+ }
+
+ // Only change the parent when we know this is a context on the plugin
+ // surface within the browser. Prevents resetting the parent on child ui
+ // displayed by plugins that have working parent-child relationships.
+ PRUnichar szClass[21];
+ bool haveClass = GetClassNameW(hWnd, szClass, NS_ARRAY_LENGTH(szClass));
+ if (!haveClass ||
+ (wcscmp(szClass, L"MozillaWindowClass") &&
+ wcscmp(szClass, L"SWFlash_Placeholder"))) {
+ // Unrecognized parent
+ return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
+ hWnd, prcRect);
+ }
+
+ // Called on an unexpected event, warn.
+ if (!sWinlessPopupSurrogateHWND) {
+ NS_WARNING(
+ "Untraced TrackPopupHookProc call! Menu might not work right!");
+ return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
+ hWnd, prcRect);
+ }
+
+ HWND surrogateHwnd = sWinlessPopupSurrogateHWND;
+ sWinlessPopupSurrogateHWND = NULL;
+
+ // Popups that don't use TPM_RETURNCMD expect a final command message
+ // when an item is selected and the context closes. Since we replace
+ // the parent, we need to forward this back to the real parent so it
+ // can act on the menu item selected.
+ bool isRetCmdCall = (uFlags & TPM_RETURNCMD);
+
+ // A little trick scrounged from chromium's code - set the focus
+ // to our surrogate parent so keyboard nav events go to the menu.
+ HWND focusHwnd = SetFocus(surrogateHwnd);
+ DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags|TPM_RETURNCMD, x, y,
+ nReserved, surrogateHwnd, prcRect);
+ if (IsWindow(focusHwnd)) {
+ SetFocus(focusHwnd);
+ }
+
+ if (!isRetCmdCall && res) {
+ SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0);
+ }
+
+ return res;
+}
+
+void
+PluginInstanceChild::InitPopupMenuHook()
+{
+ if (!(mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) ||
+ sUser32TrackPopupMenuStub)
+ return;
+
+ // Note, once WindowsDllInterceptor is initialized for a module,
+ // it remains initialized for that particular module for it's
+ // lifetime. Additional instances are needed if other modules need
+ // to be hooked.
+ sUser32Intercept.Init("user32.dll");
+ sUser32Intercept.AddHook("TrackPopupMenu", TrackPopupHookProc,
+ (void**) &sUser32TrackPopupMenuStub);
+}
+
+void
+PluginInstanceChild::CreateWinlessPopupSurrogate()
+{
+ // already initialized
+ if (mWinlessPopupSurrogateHWND)
+ return;
+
+ HWND hwnd = NULL;
+ NPError result;
+ if (!CallNPN_GetValue_NPNVnetscapeWindow(&hwnd, &result)) {
+ NS_ERROR("CallNPN_GetValue_NPNVnetscapeWindow failed.");
+ return;
+ }
+
+ mWinlessPopupSurrogateHWND =
+ CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, WS_CHILD, 0, 0,
+ 0, 0, hwnd, 0, GetModuleHandle(NULL), 0);
+ if (!mWinlessPopupSurrogateHWND) {
+ NS_ERROR("CreateWindowEx failed for winless placeholder!");
+ return;
+ }
+ return;
+}
+
+void
+PluginInstanceChild::DestroyWinlessPopupSurrogate()
+{
+ if (mWinlessPopupSurrogateHWND)
+ DestroyWindow(mWinlessPopupSurrogateHWND);
+ mWinlessPopupSurrogateHWND = NULL;
+}
+
+/* windowless handle event helpers */
+
+static bool
+NeedsNestedEventCoverage(UINT msg)
+{
+ // Events we assume some sort of modal ui *might* be generated.
+ switch (msg) {
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_CONTEXTMENU:
+ return true;
+ }
+ return false;
+}
+
+static bool
+IsMouseInputEvent(UINT msg)
+{
+ switch (msg) {
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ return true;
+ }
+ return false;
+}
+
+int16_t
+PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
+{
+ if (!mPluginIface->event)
+ return false;
+
+ // Winless Silverlight quirk: winposchanged events are not used in
+ // determining the position of the plugin within the parent window,
+ // NPP_SetWindow values are used instead. Due to shared memory dib
+ // rendering, the origin of NPP_SetWindow is 0x0, so we trap
+ // winposchanged events here and do the translation internally for
+ // mouse input events.
+ if (mQuirks & QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION) {
+ if (event.event == WM_WINDOWPOSCHANGED && event.lParam) {
+ WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(event.lParam);
+ mPluginOffset.x = pos->x;
+ mPluginOffset.y = pos->y;
+ }
+ else if (IsMouseInputEvent(event.event)) {
+ event.lParam =
+ MAKELPARAM((GET_X_LPARAM(event.lParam) - mPluginOffset.x),
+ (GET_Y_LPARAM(event.lParam) - mPluginOffset.y));
+ }
+ }
+
+ if (!NeedsNestedEventCoverage(event.event)) {
+ return mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
+ }
+
+ // Events that might generate nested event dispatch loops need
+ // special handling during delivery.
+ int16_t handled;
+
+ mNestedEventLevelDepth++;
+ PLUGIN_LOG_DEBUG(("WinlessHandleEvent start depth: %i", mNestedEventLevelDepth));
+
+ // On the first, non-reentrant call, setup our modal ui detection hook.
+ if (mNestedEventLevelDepth == 1) {
+ NS_ASSERTION(!gTempChildPointer, "valid gTempChildPointer here?");
+ gTempChildPointer = this;
+ SetNestedInputEventHook();
+ }
+
+ // TrackPopupMenu will fail if the parent window is not associated with
+ // our ui thread. So we hook TrackPopupMenu so we can hand in a surrogate
+ // parent created in the child process.
+ if ((mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default?
+ (event.event == WM_RBUTTONDOWN || // flash
+ event.event == WM_RBUTTONUP)) { // silverlight
+ sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND;
+ }
+
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
+ gTempChildPointer = NULL;
+ sWinlessPopupSurrogateHWND = NULL;
+
+ mNestedEventLevelDepth--;
+ PLUGIN_LOG_DEBUG(("WinlessHandleEvent end depth: %i", mNestedEventLevelDepth));
+
+ NS_ASSERTION(!(mNestedEventLevelDepth < 0), "mNestedEventLevelDepth < 0?");
+ if (mNestedEventLevelDepth <= 0) {
+ ResetNestedEventHook();
+ InternalCallSetNestedEventState(false);
+ }
+ return handled;
+}
+
+/* windowless drawing helpers */
+
+bool
+PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow)
+{
+ // If the surfaceHandle is empty, parent is telling us we can reuse our cached
+ // memory surface and hdc. Otherwise, we need to reset, usually due to a
+ // expanding plugin port size.
+ if (!aWindow.surfaceHandle) {
+ if (!mSharedSurfaceDib.IsValid()) {
+ return false;
+ }
+ }
+ else {
+ // Attach to the new shared surface parent handed us.
+ if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle,
+ aWindow.width, aWindow.height, 32)))
+ return false;
+ // Free any alpha extraction resources if needed. This will be reset
+ // the next time it's used.
+ AlphaExtractCacheRelease();
+ }
+
+ // NPRemoteWindow's origin is the origin of our shared dib.
+ mWindow.x = 0;
+ mWindow.y = 0;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.type = aWindow.type;
+
+ mWindow.window = reinterpret_cast<void*>(mSharedSurfaceDib.GetHDC());
+
+ if (mPluginIface->setwindow)
+ mPluginIface->setwindow(&mData, &mWindow);
+
+ return true;
+}
+
+void
+PluginInstanceChild::SharedSurfaceRelease()
+{
+ mSharedSurfaceDib.Close();
+ AlphaExtractCacheRelease();
+}
+
+/* double pass cache buffer - (rarely) used in cases where alpha extraction
+ * occurs for windowless plugins. */
+
+bool
+PluginInstanceChild::AlphaExtractCacheSetup()
+{
+ AlphaExtractCacheRelease();
+
+ mAlphaExtract.hdc = ::CreateCompatibleDC(NULL);
+
+ if (!mAlphaExtract.hdc)
+ return false;
+
+ BITMAPINFOHEADER bmih;
+ memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER));
+ bmih.biSize = sizeof(BITMAPINFOHEADER);
+ bmih.biWidth = mWindow.width;
+ bmih.biHeight = mWindow.height;
+ bmih.biPlanes = 1;
+ bmih.biBitCount = 32;
+ bmih.biCompression = BI_RGB;
+
+ void* ppvBits = nsnull;
+ mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc,
+ (BITMAPINFO*)&bmih,
+ DIB_RGB_COLORS,
+ (void**)&ppvBits,
+ NULL,
+ (unsigned long)sizeof(BITMAPINFOHEADER));
+ if (!mAlphaExtract.bmp)
+ return false;
+
+ DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp));
+ return true;
+}
+
+void
+PluginInstanceChild::AlphaExtractCacheRelease()
+{
+ if (mAlphaExtract.bmp)
+ ::DeleteObject(mAlphaExtract.bmp);
+
+ if (mAlphaExtract.hdc)
+ ::DeleteObject(mAlphaExtract.hdc);
+
+ mAlphaExtract.bmp = NULL;
+ mAlphaExtract.hdc = NULL;
+}
+
+void
+PluginInstanceChild::UpdatePaintClipRect(RECT* aRect)
+{
+ if (aRect) {
+ // Update the clip rect on our internal hdc
+ HRGN clip = ::CreateRectRgnIndirect(aRect);
+ ::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
+ ::DeleteObject(clip);
+ }
+}
+
+int16_t
+PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
+{
+ if (!mPluginIface->event)
+ return false;
+
+ RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
+
+ switch(mAlphaExtract.doublePass) {
+ case RENDER_NATIVE:
+ // pass the internal hdc to the plugin
+ UpdatePaintClipRect(pRect);
+ evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
+ return mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
+ break;
+ case RENDER_BACK_ONE:
+ // Handle a double pass render used in alpha extraction for transparent
+ // plugins. (See nsObjectFrame and gfxWindowsNativeDrawing for details.)
+ // We render twice, once to the shared dib, and once to a cache which
+ // we copy back on a second paint. These paints can't be spread across
+ // multiple rpc messages as delays cause animation frame changes.
+ if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) {
+ mAlphaExtract.doublePass = RENDER_NATIVE;
+ return false;
+ }
+
+ // See gfxWindowsNativeDrawing, color order doesn't have to match.
+ ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
+ UpdatePaintClipRect(pRect);
+ evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
+ if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
+ mAlphaExtract.doublePass = RENDER_NATIVE;
+ return false;
+ }
+
+ // Copy to cache. We render to shared dib so we don't have to call
+ // setwindow between calls (flash issue).
+ ::BitBlt(mAlphaExtract.hdc,
+ pRect->left,
+ pRect->top,
+ pRect->right - pRect->left,
+ pRect->bottom - pRect->top,
+ mSharedSurfaceDib.GetHDC(),
+ pRect->left,
+ pRect->top,
+ SRCCOPY);
+
+ ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
+ mAlphaExtract.doublePass = RENDER_NATIVE;
+ return false;
+ }
+ mAlphaExtract.doublePass = RENDER_BACK_TWO;
+ return true;
+ break;
+ case RENDER_BACK_TWO:
+ // copy our cached surface back
+ ::BitBlt(mSharedSurfaceDib.GetHDC(),
+ pRect->left,
+ pRect->top,
+ pRect->right - pRect->left,
+ pRect->bottom - pRect->top,
+ mAlphaExtract.hdc,
+ pRect->left,
+ pRect->top,
+ SRCCOPY);
+ mAlphaExtract.doublePass = RENDER_NATIVE;
+ return true;
+ break;
+ }
+ return false;
+}
+
+#endif // OS_WIN
+
+bool
+PluginInstanceChild::AnswerSetPluginFocus()
+{
+ PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION));
+
+#if defined(OS_WIN)
+ // Parent is letting us know something set focus to the plugin.
+ if (::GetFocus() == mPluginWindowHWND)
+ return true;
+ ::SetFocus(mPluginWindowHWND);
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceChild::AnswerSetPluginFocus not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerUpdateWindow()
+{
+ PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION));
+
+#if defined(OS_WIN)
+ if (mPluginWindowHWND)
+ UpdateWindow(mPluginWindowHWND);
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceChild::AnswerUpdateWindow not implemented!");
+ return false;
+#endif
+}
+
+PPluginScriptableObjectChild*
+PluginInstanceChild::AllocPPluginScriptableObject()
+{
+ AssertPluginThread();
+ return new PluginScriptableObjectChild(Proxy);
+}
+
+bool
+PluginInstanceChild::DeallocPPluginScriptableObject(
+ PPluginScriptableObjectChild* aObject)
+{
+ AssertPluginThread();
+ delete aObject;
+ return true;
+}
+
+bool
+PluginInstanceChild::RecvPPluginScriptableObjectConstructor(
+ PPluginScriptableObjectChild* aActor)
+{
+ AssertPluginThread();
+
+ // This is only called in response to the parent process requesting the
+ // creation of an actor. This actor will represent an NPObject that is
+ // created by the browser and returned to the plugin.
+ PluginScriptableObjectChild* actor =
+ static_cast<PluginScriptableObjectChild*>(aActor);
+ NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
+
+ actor->InitializeProxy();
+ NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
+
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerPBrowserStreamConstructor(
+ PBrowserStreamChild* aActor,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t* stype)
+{
+ AssertPluginThread();
+ *rv = static_cast<BrowserStreamChild*>(aActor)
+ ->StreamConstructed(mimeType, seekable, stype);
+ return true;
+}
+
+PBrowserStreamChild*
+PluginInstanceChild::AllocPBrowserStream(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t *stype)
+{
+ AssertPluginThread();
+ return new BrowserStreamChild(this, url, length, lastmodified,
+ static_cast<StreamNotifyChild*>(notifyData),
+ headers, mimeType, seekable, rv, stype);
+}
+
+bool
+PluginInstanceChild::DeallocPBrowserStream(PBrowserStreamChild* stream)
+{
+ AssertPluginThread();
+ delete stream;
+ return true;
+}
+
+PPluginStreamChild*
+PluginInstanceChild::AllocPPluginStream(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result)
+{
+ NS_RUNTIMEABORT("not callable");
+ return NULL;
+}
+
+bool
+PluginInstanceChild::DeallocPPluginStream(PPluginStreamChild* stream)
+{
+ AssertPluginThread();
+ delete stream;
+ return true;
+}
+
+PStreamNotifyChild*
+PluginInstanceChild::AllocPStreamNotify(const nsCString& url,
+ const nsCString& target,
+ const bool& post,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ AssertPluginThread();
+ NS_RUNTIMEABORT("not reached");
+ return NULL;
+}
+
+void
+StreamNotifyChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (AncestorDeletion == why && mBrowserStream) {
+ NS_ERROR("Pending NPP_URLNotify not called when closing an instance.");
+
+ // reclaim responsibility for deleting ourself
+ mBrowserStream->mStreamNotify = NULL;
+ mBrowserStream = NULL;
+ }
+}
+
+
+void
+StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs)
+{
+ NS_ASSERTION(bs, "Shouldn't be null");
+ NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?");
+
+ mBrowserStream = bs;
+}
+
+bool
+StreamNotifyChild::Recv__delete__(const NPReason& reason)
+{
+ AssertPluginThread();
+
+ if (mBrowserStream)
+ mBrowserStream->NotifyPending();
+ else
+ NPP_URLNotify(reason);
+
+ return true;
+}
+
+void
+StreamNotifyChild::NPP_URLNotify(NPReason reason)
+{
+ PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(Manager());
+
+ if (mClosure)
+ instance->mPluginIface->urlnotify(instance->GetNPP(), mURL.get(),
+ reason, mClosure);
+}
+
+bool
+PluginInstanceChild::DeallocPStreamNotify(PStreamNotifyChild* notifyData)
+{
+ AssertPluginThread();
+
+ if (!static_cast<StreamNotifyChild*>(notifyData)->mBrowserStream)
+ delete notifyData;
+ return true;
+}
+
+PluginScriptableObjectChild*
+PluginInstanceChild::GetActorForNPObject(NPObject* aObject)
+{
+ AssertPluginThread();
+ NS_ASSERTION(aObject, "Null pointer!");
+
+ if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
+ // One of ours! It's a browser-provided object.
+ ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
+ NS_ASSERTION(object->parent, "Null actor!");
+ return object->parent;
+ }
+
+ PluginScriptableObjectChild* actor =
+ PluginModuleChild::current()->GetActorForNPObject(aObject);
+ if (actor) {
+ // Plugin-provided object that we've previously wrapped.
+ return actor;
+ }
+
+ actor = new PluginScriptableObjectChild(LocalObject);
+ if (!SendPPluginScriptableObjectConstructor(actor)) {
+ NS_ERROR("Failed to send constructor message!");
+ return nsnull;
+ }
+
+ actor->InitializeLocal(aObject);
+ return actor;
+}
+
+NPError
+PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
+ NPStream** aStream)
+{
+ AssertPluginThread();
+
+ PluginStreamChild* ps = new PluginStreamChild();
+
+ NPError result;
+ CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType),
+ NullableString(aWindow), &result);
+ if (NPERR_NO_ERROR != result) {
+ *aStream = NULL;
+ PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true);
+ return result;
+ }
+
+ *aStream = &ps->mStream;
+ return NPERR_NO_ERROR;
+}
+
+void
+PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect)
+{
+ NS_ASSERTION(aInvalidRect, "Null pointer!");
+
+#ifdef OS_WIN
+ // Invalidate and draw locally for windowed plugins.
+ if (mWindow.type == NPWindowTypeWindow) {
+ NS_ASSERTION(IsWindow(mPluginWindowHWND), "Bad window?!");
+ RECT rect = { aInvalidRect->left, aInvalidRect->top,
+ aInvalidRect->right, aInvalidRect->bottom };
+ ::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
+ return;
+ }
+#endif
+
+ SendNPN_InvalidateRect(*aInvalidRect);
+}
+
+static PLDHashOperator
+InvalidateObject(DeletingObjectEntry* e, void* userArg)
+{
+ NPObject* o = e->GetKey();
+ if (!e->mDeleted && o->_class && o->_class->invalidate)
+ o->_class->invalidate(o);
+
+ return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+DeleteObject(DeletingObjectEntry* e, void* userArg)
+{
+ NPObject* o = e->GetKey();
+ if (!e->mDeleted) {
+ e->mDeleted = true;
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ {
+ int32_t refcnt = o->referenceCount;
+ while (refcnt) {
+ --refcnt;
+ NS_LOG_RELEASE(o, refcnt, "NPObject");
+ }
+ }
+#endif
+
+ PluginModuleChild::DeallocNPObject(o);
+ }
+
+ return PL_DHASH_NEXT;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+
+ nsTArray<PBrowserStreamChild*> streams;
+ ManagedPBrowserStreamChild(streams);
+
+ // First make sure none of these streams become deleted
+ for (PRUint32 i = 0; i < streams.Length(); ) {
+ if (static_cast<BrowserStreamChild*>(streams[i])->InstanceDying())
+ ++i;
+ else
+ streams.RemoveElementAt(i);
+ }
+ for (PRUint32 i = 0; i < streams.Length(); ++i)
+ static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
+
+ for (PRUint32 i = 0; i < mPendingAsyncCalls.Length(); ++i)
+ mPendingAsyncCalls[i]->Cancel();
+ mPendingAsyncCalls.TruncateLength(0);
+
+ PluginModuleChild::current()->NPP_Destroy(this);
+ mData.ndata = 0;
+
+ mDeletingHash = new nsTHashtable<DeletingObjectEntry>;
+ mDeletingHash->Init();
+ PluginModuleChild::current()->FindNPObjectsForInstance(this);
+
+ mDeletingHash->EnumerateEntries(InvalidateObject, NULL);
+ mDeletingHash->EnumerateEntries(DeleteObject, NULL);
+
+ // Null out our cached actors as they should have been killed in the
+ // PluginInstanceDestroyed call above.
+ mCachedWindowActor = nsnull;
+ mCachedElementActor = nsnull;
+
+#if defined(OS_WIN)
+ SharedSurfaceRelease();
+ ResetNestedEventHook();
+ DestroyWinlessPopupSurrogate();
+#endif
+
+ return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginInstanceChild.h
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef dom_plugins_PluginInstanceChild_h
+#define dom_plugins_PluginInstanceChild_h 1
+
+#include "mozilla/plugins/PPluginInstanceChild.h"
+#include "mozilla/plugins/PluginScriptableObjectChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
+#if defined(OS_WIN)
+#include "mozilla/gfx/SharedDIBWin.h"
+#endif
+
+#include "npfunctions.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "ChildAsyncCall.h"
+#include "nsRect.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PBrowserStreamChild;
+class BrowserStreamChild;
+class StreamNotifyChild;
+
+class PluginInstanceChild : public PPluginInstanceChild
+{
+ friend class BrowserStreamChild;
+ friend class PluginStreamChild;
+ friend class StreamNotifyChild;
+
+#ifdef OS_WIN
+ friend LRESULT CALLBACK PluginWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+#endif
+
+protected:
+ virtual bool AnswerNPP_SetWindow(const NPRemoteWindow& window);
+
+ virtual bool
+ AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(bool* needs, NPError* rv);
+ virtual bool
+ AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
+ NPError* result);
+
+ virtual bool
+ AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, NPError* result);
+
+ virtual bool
+ AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled);
+
+ NS_OVERRIDE
+ virtual bool
+ AnswerPaint(const NPRemoteEvent& event, int16_t* handled)
+ {
+ return AnswerNPP_HandleEvent(event, handled);
+ }
+
+ NS_OVERRIDE
+ virtual bool
+ RecvWindowPosChanged(const NPRemoteEvent& event);
+
+ virtual bool
+ AnswerNPP_Destroy(NPError* result);
+
+ virtual PPluginScriptableObjectChild*
+ AllocPPluginScriptableObject();
+
+ virtual bool
+ DeallocPPluginScriptableObject(PPluginScriptableObjectChild* aObject);
+
+ NS_OVERRIDE virtual bool
+ RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectChild* aActor);
+
+ virtual PBrowserStreamChild*
+ AllocPBrowserStream(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t *stype);
+
+ virtual bool
+ AnswerPBrowserStreamConstructor(
+ PBrowserStreamChild* aActor,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t* stype);
+
+ virtual bool
+ DeallocPBrowserStream(PBrowserStreamChild* stream);
+
+ virtual PPluginStreamChild*
+ AllocPPluginStream(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result);
+
+ virtual bool
+ DeallocPPluginStream(PPluginStreamChild* stream);
+
+ virtual PStreamNotifyChild*
+ AllocPStreamNotify(const nsCString& url, const nsCString& target,
+ const bool& post, const nsCString& buffer,
+ const bool& file,
+ NPError* result);
+
+ NS_OVERRIDE virtual bool
+ DeallocPStreamNotify(PStreamNotifyChild* notifyData);
+
+ virtual bool
+ AnswerSetPluginFocus();
+
+ virtual bool
+ AnswerUpdateWindow();
+
+public:
+ PluginInstanceChild(const NPPluginFuncs* aPluginIface, const nsCString& aMimeType);
+
+ virtual ~PluginInstanceChild();
+
+ bool Initialize();
+
+ NPP GetNPP()
+ {
+ return &mData;
+ }
+
+ NPError
+ NPN_GetValue(NPNVariable aVariable, void* aValue);
+
+ NPError
+ NPN_SetValue(NPPVariable aVariable, void* aValue);
+
+ PluginScriptableObjectChild*
+ GetActorForNPObject(NPObject* aObject);
+
+ NPError
+ NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
+ NPStream** aStream);
+
+ void InvalidateRect(NPRect* aInvalidRect);
+
+private:
+ friend class PluginModuleChild;
+
+ // Quirks mode support for various plugin mime types
+ enum PluginQuirks {
+ // Win32: Translate mouse input based on WM_WINDOWPOSCHANGED
+ // windowing events due to winless shared dib rendering. See
+ // WinlessHandleEvent for details.
+ QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION = 1,
+ // Win32: Hook TrackPopupMenu api so that we can swap out parent
+ // hwnds. The api will fail with parents not associated with our
+ // child ui thread. See WinlessHandleEvent for details.
+ QUIRK_WINLESS_TRACKPOPUP_HOOK = 2,
+ };
+
+ void InitQuirksModes(const nsCString& aMimeType);
+
+ NPError
+ InternalGetNPObjectForValue(NPNVariable aValue,
+ NPObject** aObject);
+
+#if defined(OS_WIN)
+ static bool RegisterWindowClass();
+ bool CreatePluginWindow();
+ void DestroyPluginWindow();
+ void ReparentPluginWindow(HWND hWndParent);
+ void SizePluginWindow(int width, int height);
+ int16_t WinlessHandleEvent(NPEvent& event);
+ void SetNestedInputEventHook();
+ void ResetNestedEventHook();
+ void SetNestedInputPumpHook();
+ void ResetPumpHooks();
+ void CreateWinlessPopupSurrogate();
+ void DestroyWinlessPopupSurrogate();
+ void InitPopupMenuHook();
+ void InternalCallSetNestedEventState(bool aState);
+ static LRESULT CALLBACK DummyWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+ static LRESULT CALLBACK PluginWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+ static VOID CALLBACK PumpTimerProc(HWND hwnd,
+ UINT uMsg,
+ UINT_PTR idEvent,
+ DWORD dwTime);
+ static LRESULT CALLBACK NestedInputEventHook(int code,
+ WPARAM wParam,
+ LPARAM lParam);
+ static LRESULT CALLBACK NestedInputPumpHook(int code,
+ WPARAM wParam,
+ LPARAM lParam);
+ static BOOL WINAPI TrackPopupHookProc(HMENU hMenu,
+ UINT uFlags,
+ int x,
+ int y,
+ int nReserved,
+ HWND hWnd,
+ CONST RECT *prcRect);
+#endif
+
+ const NPPluginFuncs* mPluginIface;
+ NPP_t mData;
+ NPWindow mWindow;
+ int mQuirks;
+
+ // Cached scriptable actors to avoid IPC churn
+ PluginScriptableObjectChild* mCachedWindowActor;
+ PluginScriptableObjectChild* mCachedElementActor;
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ NPSetWindowCallbackStruct mWsInfo;
+#elif defined(OS_WIN)
+ HWND mPluginWindowHWND;
+ WNDPROC mPluginWndProc;
+ HWND mPluginParentHWND;
+ HHOOK mNestedEventHook;
+ int mNestedEventLevelDepth;
+ bool mNestedEventState;
+ HWND mCachedWinlessPluginHWND;
+ HWND mWinlessPopupSurrogateHWND;
+ nsIntPoint mPluginSize;
+ nsIntPoint mPluginOffset;
+#endif
+
+ friend class ChildAsyncCall;
+ nsTArray<ChildAsyncCall*> mPendingAsyncCalls;
+
+ /**
+ * During destruction we enumerate all remaining scriptable objects and
+ * invalidate/delete them. Enumeration can re-enter, so maintain a
+ * hash separate from PluginModuleChild.mObjectMap.
+ */
+ nsAutoPtr< nsTHashtable<DeletingObjectEntry> > mDeletingHash;
+
+#if defined(OS_WIN)
+private:
+ // Shared dib rendering management for windowless plugins.
+ bool SharedSurfaceSetWindow(const NPRemoteWindow& aWindow);
+ int16_t SharedSurfacePaint(NPEvent& evcopy);
+ void SharedSurfaceRelease();
+ bool AlphaExtractCacheSetup();
+ void AlphaExtractCacheRelease();
+ void UpdatePaintClipRect(RECT* aRect);
+
+private:
+ enum {
+ RENDER_NATIVE,
+ RENDER_BACK_ONE,
+ RENDER_BACK_TWO
+ };
+ gfx::SharedDIBWin mSharedSurfaceDib;
+ struct {
+ PRUint32 doublePassEvent;
+ PRUint16 doublePass;
+ HDC hdc;
+ HBITMAP bmp;
+ } mAlphaExtract;
+#endif // defined(OS_WIN)
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_PluginInstanceChild_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -0,0 +1,1136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jim Mathies <jmathies@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "PluginInstanceParent.h"
+
+#include "BrowserStreamParent.h"
+#include "PluginModuleParent.h"
+#include "PluginStreamParent.h"
+#include "StreamNotifyParent.h"
+#include "npfunctions.h"
+#include "nsAutoPtr.h"
+
+#if defined(OS_WIN)
+#include <windowsx.h>
+
+// Plugin focus event for widget.
+extern const PRUnichar* kOOPPPluginFocusEventId;
+UINT gOOPPPluginFocusEvent =
+ RegisterWindowMessage(kOOPPPluginFocusEventId);
+UINT gOOPPSpinNativeLoopEvent =
+ RegisterWindowMessage(L"SyncChannel Spin Inner Loop Message");
+UINT gOOPPStopNativeLoopEvent =
+ RegisterWindowMessage(L"SyncChannel Stop Inner Loop Message");
+#elif defined(MOZ_WIDGET_GTK2)
+#include <gdk/gdk.h>
+#endif
+
+using namespace mozilla::plugins;
+
+PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
+ NPP npp,
+ const NPNetscapeFuncs* npniface)
+ : mParent(parent)
+ , mNPP(npp)
+ , mNPNIface(npniface)
+ , mWindowType(NPWindowTypeWindow)
+#if defined(OS_WIN)
+ , mPluginHWND(NULL)
+ , mPluginWndProc(NULL)
+ , mNestedEventState(false)
+#endif // defined(XP_WIN)
+{
+}
+
+PluginInstanceParent::~PluginInstanceParent()
+{
+ if (mNPP)
+ mNPP->pdata = NULL;
+
+#if defined(OS_WIN)
+ NS_ASSERTION(!(mPluginHWND || mPluginWndProc),
+ "Subclass was not reset correctly before the dtor was reached!");
+#endif
+}
+
+bool
+PluginInstanceParent::Init()
+{
+ return !!mScriptableObjects.Init();
+}
+
+namespace {
+
+PLDHashOperator
+ActorCollect(const void* aKey,
+ PluginScriptableObjectParent* aData,
+ void* aUserData)
+{
+ nsTArray<PluginScriptableObjectParent*>* objects =
+ reinterpret_cast<nsTArray<PluginScriptableObjectParent*>*>(aUserData);
+ return objects->AppendElement(aData) ? PL_DHASH_NEXT : PL_DHASH_STOP;
+}
+
+} // anonymous namespace
+
+void
+PluginInstanceParent::ActorDestroy(ActorDestroyReason why)
+{
+#if defined(OS_WIN)
+ if (why == AbnormalShutdown) {
+ // If the plugin process crashes, this is the only
+ // chance we get to destroy resources.
+ SharedSurfaceRelease();
+ UnsubclassPluginWindow();
+ // If we crashed in a modal loop in the child, reset
+ // the rpc event spin loop state.
+ if (mNestedEventState) {
+ mNestedEventState = false;
+ PostThreadMessage(GetCurrentThreadId(),
+ gOOPPStopNativeLoopEvent,
+ 0, 0);
+ }
+ }
+#endif
+}
+
+NPError
+PluginInstanceParent::Destroy()
+{
+ NPError retval;
+ if (!CallNPP_Destroy(&retval))
+ retval = NPERR_GENERIC_ERROR;
+
+#if defined(OS_WIN)
+ SharedSurfaceRelease();
+ UnsubclassPluginWindow();
+ if (mNestedEventState) {
+ mNestedEventState = false;
+ PostThreadMessage(GetCurrentThreadId(),
+ gOOPPStopNativeLoopEvent,
+ 0, 0);
+ }
+#endif
+
+ return retval;
+}
+
+PBrowserStreamParent*
+PluginInstanceParent::AllocPBrowserStream(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyParent* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t *stype)
+{
+ NS_RUNTIMEABORT("Not reachable");
+ return NULL;
+}
+
+bool
+PluginInstanceParent::DeallocPBrowserStream(PBrowserStreamParent* stream)
+{
+ delete stream;
+ return true;
+}
+
+PPluginStreamParent*
+PluginInstanceParent::AllocPPluginStream(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result)
+{
+ return new PluginStreamParent(this, mimeType, target, result);
+}
+
+bool
+PluginInstanceParent::DeallocPPluginStream(PPluginStreamParent* stream)
+{
+ delete stream;
+ return true;
+}
+
+#ifdef MOZ_X11
+static Display* GetXDisplay() {
+# ifdef MOZ_WIDGET_GTK2
+ return GDK_DISPLAY();
+# elif defined(MOZ_WIDGET_QT)
+ return QX11Info::display();
+# endif
+}
+#endif
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVjavascriptEnabledBool(
+ bool* value,
+ NPError* result)
+{
+ NPBool v;
+ *result = mNPNIface->getvalue(mNPP, NPNVjavascriptEnabledBool, &v);
+ *value = v;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVisOfflineBool(bool* value,
+ NPError* result)
+{
+ NPBool v;
+ *result = mNPNIface->getvalue(mNPP, NPNVisOfflineBool, &v);
+ *value = v;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value,
+ NPError* result)
+{
+#ifdef XP_WIN
+ HWND id;
+#elif defined(MOZ_X11)
+ XID id;
+#else
+ return false;
+#endif
+
+ *result = mNPNIface->getvalue(mNPP, NPNVnetscapeWindow, &id);
+ *value = id;
+ return true;
+}
+
+bool
+PluginInstanceParent::InternalGetValueForNPObject(
+ NPNVariable aVariable,
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult)
+{
+ NPObject* npobject;
+ NPError result = mNPNIface->getvalue(mNPP, aVariable, (void*)&npobject);
+ if (result == NPERR_NO_ERROR) {
+ NS_ASSERTION(npobject, "Shouldn't return null and NPERR_NO_ERROR!");
+
+ PluginScriptableObjectParent* actor = GetActorForNPObject(npobject);
+ mNPNIface->releaseobject(npobject);
+ if (actor) {
+ *aValue = actor;
+ *aResult = NPERR_NO_ERROR;
+ return true;
+ }
+
+ NS_ERROR("Failed to get actor!");
+ result = NPERR_GENERIC_ERROR;
+ }
+
+ *aValue = nsnull;
+ *aResult = result;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVWindowNPObject(
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult)
+{
+ return InternalGetValueForNPObject(NPNVWindowNPObject, aValue, aResult);
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVPluginElementNPObject(
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult)
+{
+ return InternalGetValueForNPObject(NPNVPluginElementNPObject, aValue,
+ aResult);
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVprivateModeBool(bool* value,
+ NPError* result)
+{
+ NPBool v;
+ *result = mNPNIface->getvalue(mNPP, NPNVprivateModeBool, &v);
+ *value = v;
+ return true;
+}
+
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginWindow(
+ const bool& windowed, NPError* result)
+{
+ NPBool isWindowed = windowed;
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginWindowBool,
+ (void*)isWindowed);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginTransparent(
+ const bool& transparent, NPError* result)
+{
+ NPBool isTransparent = transparent;
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginTransparentBool,
+ (void*)isTransparent);
+ return true;
+}
+
+
+bool
+PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url,
+ const nsCString& target,
+ NPError* result)
+{
+ *result = mNPNIface->geturl(mNPP,
+ NullableStringGet(url),
+ NullableStringGet(target));
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_PostURL(const nsCString& url,
+ const nsCString& target,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ *result = mNPNIface->posturl(mNPP, url.get(), NullableStringGet(target),
+ buffer.Length(), buffer.get(), file);
+ return true;
+}
+
+PStreamNotifyParent*
+PluginInstanceParent::AllocPStreamNotify(const nsCString& url,
+ const nsCString& target,
+ const bool& post,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ return new StreamNotifyParent();
+}
+
+bool
+PluginInstanceParent::AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor,
+ const nsCString& url,
+ const nsCString& target,
+ const bool& post,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ bool streamDestroyed = false;
+ static_cast<StreamNotifyParent*>(actor)->
+ SetDestructionFlag(&streamDestroyed);
+
+ if (!post) {
+ *result = mNPNIface->geturlnotify(mNPP,
+ NullableStringGet(url),
+ NullableStringGet(target),
+ actor);
+ }
+ else {
+ *result = mNPNIface->posturlnotify(mNPP,
+ NullableStringGet(url),
+ NullableStringGet(target),
+ buffer.Length(),
+ NullableStringGet(buffer),
+ file, actor);
+ }
+
+ if (!streamDestroyed) {
+ static_cast<StreamNotifyParent*>(actor)->ClearDestructionFlag();
+ if (*result != NPERR_NO_ERROR)
+ PStreamNotifyParent::Send__delete__(actor, NPERR_GENERIC_ERROR);
+ }
+
+ return true;
+}
+
+bool
+PluginInstanceParent::DeallocPStreamNotify(PStreamNotifyParent* notifyData)
+{
+ delete notifyData;
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvNPN_InvalidateRect(const NPRect& rect)
+{
+ mNPNIface->invalidaterect(mNPP, const_cast<NPRect*>(&rect));
+ return true;
+}
+
+NPError
+PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
+{
+ PLUGIN_LOG_DEBUG(("%s (aWindow=%p)", FULLFUNCTION, (void*) aWindow));
+
+ NS_ENSURE_TRUE(aWindow, NPERR_GENERIC_ERROR);
+
+ NPRemoteWindow window;
+ mWindowType = aWindow->type;
+
+#if defined(OS_WIN)
+ // On windowless controls, reset the shared memory surface as needed.
+ if (mWindowType == NPWindowTypeDrawable) {
+ // SharedSurfaceSetWindow will take care of NPRemoteWindow.
+ if (!SharedSurfaceSetWindow(aWindow, window)) {
+ return NPERR_OUT_OF_MEMORY_ERROR;
+ }
+ }
+ else {
+ SubclassPluginWindow(reinterpret_cast<HWND>(aWindow->window));
+
+ window.window = reinterpret_cast<unsigned long>(aWindow->window);
+ window.x = aWindow->x;
+ window.y = aWindow->y;
+ window.width = aWindow->width;
+ window.height = aWindow->height;
+ window.type = aWindow->type;
+ }
+#else
+ window.window = reinterpret_cast<unsigned long>(aWindow->window);
+ window.x = aWindow->x;
+ window.y = aWindow->y;
+ window.width = aWindow->width;
+ window.height = aWindow->height;
+ window.clipRect = aWindow->clipRect; // MacOS specific
+ window.type = aWindow->type;
+#endif
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ const NPSetWindowCallbackStruct* ws_info =
+ static_cast<NPSetWindowCallbackStruct*>(aWindow->ws_info);
+ window.visualID = ws_info->visual ? ws_info->visual->visualid : None;
+ window.colormap = ws_info->colormap;
+#endif
+
+ if (!CallNPP_SetWindow(window))
+ return NPERR_GENERIC_ERROR;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError
+PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
+ void* _retval)
+{
+ switch (aVariable) {
+
+#ifdef MOZ_X11
+ case NPPVpluginNeedsXEmbed: {
+ bool needsXEmbed;
+ NPError rv;
+
+ if (!CallNPP_GetValue_NPPVpluginNeedsXEmbed(&needsXEmbed, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ (*(NPBool*)_retval) = needsXEmbed;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPPVpluginScriptableNPObject: {
+ PPluginScriptableObjectParent* actor;
+ NPError rv;
+ if (!CallNPP_GetValue_NPPVpluginScriptableNPObject(&actor, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ if (!actor) {
+ NS_ERROR("NPPVpluginScriptableNPObject succeeded but null.");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
+ if (!npn) {
+ NS_WARNING("No netscape functions?!");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ NPObject* object =
+ static_cast<PluginScriptableObjectParent*>(actor)->GetObject(true);
+ NS_ASSERTION(object, "This shouldn't ever be null!");
+
+ (*(NPObject**)_retval) = npn->retainobject(object);
+ return NPERR_NO_ERROR;
+ }
+
+ default:
+ PR_LOG(gPluginLog, PR_LOG_WARNING,
+ ("In PluginInstanceParent::NPP_GetValue: Unhandled NPPVariable %i (%s)",
+ (int) aVariable, NPPVariableToString(aVariable)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError
+PluginInstanceParent::NPP_SetValue(NPNVariable variable, void* value)
+{
+ switch (variable) {
+ case NPNVprivateModeBool:
+ NPError result;
+ if (!CallNPP_SetValue_NPNVprivateModeBool(*static_cast<NPBool*>(value),
+ &result))
+ return NPERR_GENERIC_ERROR;
+
+ return result;
+
+ default:
+ NS_ERROR("Unhandled NPNVariable in NPP_SetValue");
+ PR_LOG(gPluginLog, PR_LOG_WARNING,
+ ("In PluginInstanceParent::NPP_SetValue: Unhandled NPNVariable %i (%s)",
+ (int) variable, NPNVariableToString(variable)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+int16_t
+PluginInstanceParent::NPP_HandleEvent(void* event)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ NPEvent* npevent = reinterpret_cast<NPEvent*>(event);
+ NPRemoteEvent npremoteevent;
+ npremoteevent.event = *npevent;
+ int16_t handled = 0;
+
+#if defined(OS_WIN)
+ if (mWindowType == NPWindowTypeDrawable) {
+ switch(npevent->event) {
+ case WM_PAINT:
+ {
+ RECT rect;
+ SharedSurfaceBeforePaint(rect, npremoteevent);
+ CallPaint(npremoteevent, &handled);
+ SharedSurfaceAfterPaint(npevent);
+ return handled;
+ }
+ break;
+
+ case WM_KILLFOCUS:
+ {
+ // When the user selects fullscreen mode in Flash video players,
+ // WM_KILLFOCUS will be delayed by deferred event processing:
+ // WM_LBUTTONUP results in a call to CreateWindow within Flash,
+ // which fires WM_KILLFOCUS. Delayed delivery causes Flash to
+ // misinterpret the event, dropping back out of fullscreen. Trap
+ // this event and drop it.
+ PRUnichar szClass[26];
+ HWND hwnd = GetForegroundWindow();
+ if (hwnd && hwnd != mPluginHWND &&
+ GetClassNameW(hwnd, szClass,
+ sizeof(szClass)/sizeof(PRUnichar)) &&
+ !wcscmp(szClass, L"ShockwaveFlashFullScreen")) {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ // We send this in nsObjectFrame just before painting
+ SendWindowPosChanged(npremoteevent);
+ // nsObjectFrame doesn't care whether we handle this
+ // or not, just returning 1 for good hygiene
+ return 1;
+ }
+ break;
+ }
+ }
+#endif
+
+#if defined(MOZ_X11)
+ switch (npevent->type) {
+ case GraphicsExpose:
+ PLUGIN_LOG_DEBUG((" schlepping drawable 0x%lx across the pipe\n",
+ npevent->xgraphicsexpose.drawable));
+ // Make sure the X server has created the Drawable and completes any
+ // drawing before the plugin draws on top.
+ //
+ // XSync() waits for the X server to complete. Really this parent
+ // process does not need to wait; the child is the process that needs
+ // to wait. A possibly-slightly-better alternative would be to send
+ // an X event to the child that the child would wait for.
+ XSync(GetXDisplay(), False);
+ break;
+ case ButtonPress:
+ // Release any active pointer grab so that the plugin X client can
+ // grab the pointer if it wishes.
+ Display *dpy = GetXDisplay();
+# ifdef MOZ_WIDGET_GTK2
+ // GDK attempts to (asynchronously) track whether there is an active
+ // grab so ungrab through GDK.
+ gdk_pointer_ungrab(npevent->xbutton.time);
+# else
+ XUngrabPointer(dpy, npevent->xbutton.time);
+# endif
+ // Wait for the ungrab to complete.
+ XSync(dpy, False);
+ break;
+
+ return CallPaint(npremoteevent, &handled) ? handled : 0;
+ }
+#endif
+
+ if (!CallNPP_HandleEvent(npremoteevent, &handled))
+ return 0; // no good way to handle errors here...
+
+ return handled;
+}
+
+NPError
+PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype)
+{
+ PLUGIN_LOG_DEBUG(("%s (type=%s, stream=%p, seekable=%i)",
+ FULLFUNCTION, (char*) type, (void*) stream, (int) seekable));
+
+ BrowserStreamParent* bs = new BrowserStreamParent(this, stream);
+
+ NPError err;
+ if (!CallPBrowserStreamConstructor(bs,
+ NullableString(stream->url),
+ stream->end,
+ stream->lastmodified,
+ static_cast<PStreamNotifyParent*>(stream->notifyData),
+ NullableString(stream->headers),
+ NullableString(type), seekable,
+ &err, stype))
+ return NPERR_GENERIC_ERROR;
+
+ if (NPERR_NO_ERROR != err)
+ PBrowserStreamParent::Send__delete__(bs);
+
+ return err;
+}
+
+NPError
+PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason)
+{
+ PLUGIN_LOG_DEBUG(("%s (stream=%p, reason=%i)",
+ FULLFUNCTION, (void*) stream, (int) reason));
+
+ AStream* s = static_cast<AStream*>(stream->pdata);
+ if (s->IsBrowserStream()) {
+ BrowserStreamParent* sp =
+ static_cast<BrowserStreamParent*>(s);
+ if (sp->mNPP != this)
+ NS_RUNTIMEABORT("Mismatched plugin data");
+
+ sp->NPP_DestroyStream(reason);
+ return NPERR_NO_ERROR;
+ }
+ else {
+ PluginStreamParent* sp =
+ static_cast<PluginStreamParent*>(s);
+ if (sp->mInstance != this)
+ NS_RUNTIMEABORT("Mismatched plugin data");
+
+ PPluginStreamParent::Call__delete__(sp, reason, false);
+ return NPERR_NO_ERROR;
+ }
+}
+
+void
+PluginInstanceParent::NPP_Print(NPPrint* platformPrint)
+{
+ // TODO: implement me
+ NS_ERROR("Not implemented");
+}
+
+PPluginScriptableObjectParent*
+PluginInstanceParent::AllocPPluginScriptableObject()
+{
+ return new PluginScriptableObjectParent(Proxy);
+}
+
+#ifdef DEBUG
+namespace {
+
+struct ActorSearchData
+{
+ PluginScriptableObjectParent* actor;
+ bool found;
+};
+
+PLDHashOperator
+ActorSearch(const void* aKey,
+ PluginScriptableObjectParent* aData,
+ void* aUserData)
+{
+ ActorSearchData* asd = reinterpret_cast<ActorSearchData*>(aUserData);
+ if (asd->actor == aData) {
+ asd->found = true;
+ return PL_DHASH_STOP;
+ }
+ return PL_DHASH_NEXT;
+}
+
+} // anonymous namespace
+#endif // DEBUG
+
+bool
+PluginInstanceParent::DeallocPPluginScriptableObject(
+ PPluginScriptableObjectParent* aObject)
+{
+ PluginScriptableObjectParent* actor =
+ static_cast<PluginScriptableObjectParent*>(aObject);
+
+ NPObject* object = actor->GetObject(false);
+ if (object) {
+ NS_ASSERTION(mScriptableObjects.Get(object, nsnull),
+ "NPObject not in the hash!");
+ mScriptableObjects.Remove(object);
+ }
+#ifdef DEBUG
+ else {
+ ActorSearchData asd = { actor, false };
+ mScriptableObjects.EnumerateRead(ActorSearch, &asd);
+ NS_ASSERTION(!asd.found, "Actor in the hash with a null NPObject!");
+ }
+#endif
+
+ delete actor;
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvPPluginScriptableObjectConstructor(
+ PPluginScriptableObjectParent* aActor)
+{
+ // This is only called in response to the child process requesting the
+ // creation of an actor. This actor will represent an NPObject that is
+ // created by the plugin and returned to the browser.
+ PluginScriptableObjectParent* actor =
+ static_cast<PluginScriptableObjectParent*>(aActor);
+ NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
+
+ actor->InitializeProxy();
+ NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
+
+ return true;
+}
+
+void
+PluginInstanceParent::NPP_URLNotify(const char* url, NPReason reason,
+ void* notifyData)
+{
+ PLUGIN_LOG_DEBUG(("%s (%s, %i, %p)",
+ FULLFUNCTION, url, (int) reason, notifyData));
+
+ PStreamNotifyParent* streamNotify =
+ static_cast<PStreamNotifyParent*>(notifyData);
+ PStreamNotifyParent::Send__delete__(streamNotify, reason);
+}
+
+bool
+PluginInstanceParent::RegisterNPObjectForActor(
+ NPObject* aObject,
+ PluginScriptableObjectParent* aActor)
+{
+ NS_ASSERTION(aObject && aActor, "Null pointers!");
+ NS_ASSERTION(mScriptableObjects.IsInitialized(), "Hash not initialized!");
+ NS_ASSERTION(!mScriptableObjects.Get(aObject, nsnull), "Duplicate entry!");
+ return !!mScriptableObjects.Put(aObject, aActor);
+}
+
+void
+PluginInstanceParent::UnregisterNPObject(NPObject* aObject)
+{
+ NS_ASSERTION(aObject, "Null pointer!");
+ NS_ASSERTION(mScriptableObjects.IsInitialized(), "Hash not initialized!");
+ NS_ASSERTION(mScriptableObjects.Get(aObject, nsnull), "Unknown entry!");
+ mScriptableObjects.Remove(aObject);
+}
+
+PluginScriptableObjectParent*
+PluginInstanceParent::GetActorForNPObject(NPObject* aObject)
+{
+ NS_ASSERTION(aObject, "Null pointer!");
+
+ if (aObject->_class == PluginScriptableObjectParent::GetClass()) {
+ // One of ours!
+ ParentNPObject* object = static_cast<ParentNPObject*>(aObject);
+ NS_ASSERTION(object->parent, "Null actor!");
+ return object->parent;
+ }
+
+ PluginScriptableObjectParent* actor;
+ if (mScriptableObjects.Get(aObject, &actor)) {
+ return actor;
+ }
+
+ actor = new PluginScriptableObjectParent(LocalObject);
+ if (!actor) {
+ NS_ERROR("Out of memory!");
+ return nsnull;
+ }
+
+ if (!SendPPluginScriptableObjectConstructor(actor)) {
+ NS_WARNING("Failed to send constructor message!");
+ return nsnull;
+ }
+
+ actor->InitializeLocal(aObject);
+ return actor;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_PushPopupsEnabledState(const bool& aState,
+ bool* aSuccess)
+{
+ *aSuccess = mNPNIface->pushpopupsenabledstate(mNPP, aState ? 1 : 0);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_PopPopupsEnabledState(bool* aSuccess)
+{
+ *aSuccess = mNPNIface->poppopupsenabledstate(mNPP);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ nsCString* value,
+ NPError* result)
+{
+ char* v;
+ uint32_t len;
+
+ *result = mNPNIface->getvalueforurl(mNPP, (NPNURLVariable) variable,
+ url.get(), &v, &len);
+ if (NPERR_NO_ERROR == *result)
+ value->Adopt(v, len);
+
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ const nsCString& value,
+ NPError* result)
+{
+ *result = mNPNIface->setvalueforurl(mNPP, (NPNURLVariable) variable,
+ url.get(), value.get(),
+ value.Length());
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetAuthenticationInfo(const nsCString& protocol,
+ const nsCString& host,
+ const int32_t& port,
+ const nsCString& scheme,
+ const nsCString& realm,
+ nsCString* username,
+ nsCString* password,
+ NPError* result)
+{
+ char* u;
+ uint32_t ulen;
+ char* p;
+ uint32_t plen;
+
+ *result = mNPNIface->getauthenticationinfo(mNPP, protocol.get(),
+ host.get(), port,
+ scheme.get(), realm.get(),
+ &u, &ulen, &p, &plen);
+ if (NPERR_NO_ERROR == *result) {
+ username->Adopt(u, ulen);
+ password->Adopt(p, plen);
+ }
+ return true;
+}
+
+#if defined(OS_WIN)
+
+/*
+ plugin focus changes between processes
+
+ focus from dom -> child:
+ Focs manager calls on widget to set the focus on the window.
+ We pick up the resulting wm_setfocus event here, and forward
+ that over ipc to the child which calls set focus on itself.
+
+ focus from child -> focus manager:
+ Child picks up the local wm_setfocus and sends it via ipc over
+ here. We then post a custom event to widget/src/windows/nswindow
+ which fires off a gui event letting the browser know.
+*/
+
+static const PRUnichar kPluginInstanceParentProperty[] =
+ L"PluginInstanceParentProperty";
+
+// static
+LRESULT CALLBACK
+PluginInstanceParent::PluginWindowHookProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ PluginInstanceParent* self = reinterpret_cast<PluginInstanceParent*>(
+ ::GetPropW(hWnd, kPluginInstanceParentProperty));
+ if (!self) {
+ NS_NOTREACHED("PluginInstanceParent::PluginWindowHookProc null this ptr!");
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+
+ NS_ASSERTION(self->mPluginHWND == hWnd, "Wrong window!");
+
+ switch (message) {
+ case WM_SETFOCUS:
+ // Let the child plugin window know it should take focus.
+ self->CallSetPluginFocus();
+ break;
+
+ case WM_CLOSE:
+ self->UnsubclassPluginWindow();
+ break;
+ }
+
+ return ::CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
+ lParam);
+}
+
+void
+PluginInstanceParent::SubclassPluginWindow(HWND aWnd)
+{
+ NS_ASSERTION(!(mPluginHWND && aWnd != mPluginHWND),
+ "PluginInstanceParent::SubclassPluginWindow hwnd is not our window!");
+
+ if (!mPluginHWND) {
+ mPluginHWND = aWnd;
+ mPluginWndProc =
+ (WNDPROC)::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(PluginWindowHookProc));
+ bool bRes = ::SetPropW(mPluginHWND, kPluginInstanceParentProperty, this);
+ NS_ASSERTION(mPluginWndProc,
+ "PluginInstanceParent::SubclassPluginWindow failed to set subclass!");
+ NS_ASSERTION(bRes,
+ "PluginInstanceParent::SubclassPluginWindow failed to set prop!");
+ }
+}
+
+void
+PluginInstanceParent::UnsubclassPluginWindow()
+{
+ if (mPluginHWND && mPluginWndProc) {
+ ::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(mPluginWndProc));
+
+ ::RemovePropW(mPluginHWND, kPluginInstanceParentProperty);
+
+ mPluginWndProc = NULL;
+ mPluginHWND = NULL;
+ }
+}
+
+/* windowless drawing helpers */
+
+/*
+ * Origin info:
+ *
+ * windowless, offscreen:
+ *
+ * WM_WINDOWPOSCHANGED: origin is relative to container
+ * setwindow: origin is 0,0
+ * WM_PAINT: origin is 0,0
+ *
+ * windowless, native:
+ *
+ * WM_WINDOWPOSCHANGED: origin is relative to container
+ * setwindow: origin is relative to container
+ * WM_PAINT: origin is relative to container
+ *
+ * PluginInstanceParent:
+ *
+ * painting: mPluginPort (nsIntRect, saved in SetWindow)
+ */
+
+void
+PluginInstanceParent::SharedSurfaceRelease()
+{
+ mSharedSurfaceDib.Close();
+}
+
+bool
+PluginInstanceParent::SharedSurfaceSetWindow(const NPWindow* aWindow,
+ NPRemoteWindow& aRemoteWindow)
+{
+ aRemoteWindow.window = nsnull;
+ aRemoteWindow.x = 0;
+ aRemoteWindow.y = 0;
+ aRemoteWindow.width = aWindow->width;
+ aRemoteWindow.height = aWindow->height;
+ aRemoteWindow.type = aWindow->type;
+
+ nsIntRect newPort(aWindow->x, aWindow->y, aWindow->width, aWindow->height);
+
+ // save the the rect location within the browser window.
+ mPluginPort = newPort;
+
+ // move the port to our shared surface origin
+ newPort.MoveTo(0,0);
+
+ // check to see if we have the room in shared surface
+ if (mSharedSurfaceDib.IsValid() && mSharedSize.Contains(newPort)) {
+ // ok to paint
+ aRemoteWindow.surfaceHandle = 0;
+ return true;
+ }
+
+ // allocate a new shared surface
+ SharedSurfaceRelease();
+ if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(aWindow->window),
+ newPort.width, newPort.height, 32)))
+ return false;
+
+ // save the new shared surface size we just allocated
+ mSharedSize = newPort;
+
+ base::SharedMemoryHandle handle;
+ if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle)))
+ return false;
+
+ aRemoteWindow.surfaceHandle = handle;
+
+ return true;
+}
+
+void
+PluginInstanceParent::SharedSurfaceBeforePaint(RECT& rect,
+ NPRemoteEvent& npremoteevent)
+{
+ RECT* dr = (RECT*)npremoteevent.event.lParam;
+ HDC parentHdc = (HDC)npremoteevent.event.wParam;
+
+ nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top);
+ dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); // should always be smaller than dirtyRect
+
+ ::BitBlt(mSharedSurfaceDib.GetHDC(),
+ dirtyRect.x,
+ dirtyRect.y,
+ dirtyRect.width,
+ dirtyRect.height,
+ parentHdc,
+ dr->left,
+ dr->top,
+ SRCCOPY);
+
+ // setup the translated dirty rect we'll send to the child
+ rect.left = dirtyRect.x;
+ rect.top = dirtyRect.y;
+ rect.right = dirtyRect.x + dirtyRect.width;
+ rect.bottom = dirtyRect.y + dirtyRect.height;
+
+ npremoteevent.event.wParam = WPARAM(0);
+ npremoteevent.event.lParam = LPARAM(&rect);
+}
+
+void
+PluginInstanceParent::SharedSurfaceAfterPaint(NPEvent* npevent)
+{
+ RECT* dr = (RECT*)npevent->lParam;
+ HDC parentHdc = (HDC)npevent->wParam;
+
+ nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top);
+ dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y);
+
+ // src copy the shared dib into the parent surface we are handed.
+ ::BitBlt(parentHdc,
+ dr->left,
+ dr->top,
+ dirtyRect.width,
+ dirtyRect.height,
+ mSharedSurfaceDib.GetHDC(),
+ dirtyRect.x,
+ dirtyRect.y,
+ SRCCOPY);
+}
+
+#endif // defined(OS_WIN)
+
+bool
+PluginInstanceParent::AnswerPluginGotFocus()
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ // Currently only in use on windows - an rpc event we receive from the
+ // child when it's plugin window (or one of it's children) receives keyboard
+ // focus. We forward the event down to widget so the dom/focus manager can
+ // be updated.
+#if defined(OS_WIN)
+ ::SendMessage(mPluginHWND, gOOPPPluginFocusEvent, 0, 0);
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceParent::AnswerPluginGotFocus not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceParent::RecvSetNestedEventState(const bool& aState)
+{
+ PLUGIN_LOG_DEBUG(("%s state=%i", FULLFUNCTION, (int)aState));
+#if defined(OS_WIN)
+ PostThreadMessage(GetCurrentThreadId(), aState ?
+ gOOPPSpinNativeLoopEvent : gOOPPStopNativeLoopEvent, 0, 0);
+ mNestedEventState = aState;
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::AnswerSetNestedEventState not implemented!");
+ return false;
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginInstanceParent.h
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ * Chris Jones <jones.chris.g@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef dom_plugins_PluginInstanceParent_h
+#define dom_plugins_PluginInstanceParent_h 1
+
+#include "mozilla/plugins/PPluginInstanceParent.h"
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+#if defined(OS_WIN)
+#include "mozilla/gfx/SharedDIBWin.h"
+#endif
+
+#include "npfunctions.h"
+#include "nsAutoPtr.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsRect.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PBrowserStreamParent;
+class PluginModuleParent;
+
+class PluginInstanceParent : public PPluginInstanceParent
+{
+ friend class PluginModuleParent;
+ friend class BrowserStreamParent;
+ friend class PluginStreamParent;
+
+public:
+ PluginInstanceParent(PluginModuleParent* parent,
+ NPP npp,
+ const NPNetscapeFuncs* npniface);
+
+ virtual ~PluginInstanceParent();
+
+ bool Init();
+ NPError Destroy();
+
+ NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why);
+
+ virtual PPluginScriptableObjectParent*
+ AllocPPluginScriptableObject();
+
+ NS_OVERRIDE virtual bool
+ RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectParent* aActor);
+
+ virtual bool
+ DeallocPPluginScriptableObject(PPluginScriptableObjectParent* aObject);
+ virtual PBrowserStreamParent*
+ AllocPBrowserStream(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyParent* notifyData,
+ const nsCString& headers,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t *stype);
+ virtual bool
+ DeallocPBrowserStream(PBrowserStreamParent* stream);
+
+ virtual PPluginStreamParent*
+ AllocPPluginStream(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result);
+ virtual bool
+ DeallocPPluginStream(PPluginStreamParent* stream);
+
+ virtual bool
+ AnswerNPN_GetValue_NPNVjavascriptEnabledBool(bool* value, NPError* result);
+ virtual bool
+ AnswerNPN_GetValue_NPNVisOfflineBool(bool* value, NPError* result);
+ virtual bool
+ AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value,
+ NPError* result);
+ virtual bool
+ AnswerNPN_GetValue_NPNVWindowNPObject(
+ PPluginScriptableObjectParent** value,
+ NPError* result);
+ virtual bool
+ AnswerNPN_GetValue_NPNVPluginElementNPObject(
+ PPluginScriptableObjectParent** value,
+ NPError* result);
+ virtual bool
+ AnswerNPN_GetValue_NPNVprivateModeBool(bool* value, NPError* result);
+
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginWindow(const bool& windowed, NPError* result);
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginTransparent(const bool& transparent,
+ NPError* result);
+
+ virtual bool
+ AnswerNPN_GetURL(const nsCString& url, const nsCString& target,
+ NPError *result);
+
+ virtual bool
+ AnswerNPN_PostURL(const nsCString& url, const nsCString& target,
+ const nsCString& buffer, const bool& file,
+ NPError* result);
+
+ virtual PStreamNotifyParent*
+ AllocPStreamNotify(const nsCString& url, const nsCString& target,
+ const bool& post, const nsCString& buffer,
+ const bool& file,
+ NPError* result);
+
+ NS_OVERRIDE virtual bool
+ AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor,
+ const nsCString& url,
+ const nsCString& target,
+ const bool& post, const nsCString& buffer,
+ const bool& file,
+ NPError* result);
+
+ virtual bool
+ DeallocPStreamNotify(PStreamNotifyParent* notifyData);
+
+ virtual bool
+ RecvNPN_InvalidateRect(const NPRect& rect);
+
+ virtual bool
+ AnswerNPN_PushPopupsEnabledState(const bool& aState,
+ bool* aSuccess);
+
+ virtual bool
+ AnswerNPN_PopPopupsEnabledState(bool* aSuccess);
+
+ NS_OVERRIDE virtual bool
+ AnswerNPN_GetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ nsCString* value, NPError* result);
+
+ NS_OVERRIDE virtual bool
+ AnswerNPN_SetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ const nsCString& value, NPError* result);
+
+ NS_OVERRIDE virtual bool
+ AnswerNPN_GetAuthenticationInfo(const nsCString& protocol,
+ const nsCString& host,
+ const int32_t& port,
+ const nsCString& scheme,
+ const nsCString& realm,
+ nsCString* username,
+ nsCString* password,
+ NPError* result);
+
+ NPError NPP_SetWindow(const NPWindow* aWindow);
+
+ NPError NPP_GetValue(NPPVariable variable, void* retval);
+ NPError NPP_SetValue(NPNVariable variable, void* value);
+
+ NPError NPP_NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype);
+ NPError NPP_DestroyStream(NPStream* stream, NPReason reason);
+
+ void NPP_Print(NPPrint* platformPrint);
+
+ int16_t NPP_HandleEvent(void* event);
+
+ void NPP_URLNotify(const char* url, NPReason reason, void* notifyData);
+
+ PluginModuleParent* Module()
+ {
+ return mParent;
+ }
+
+ const NPNetscapeFuncs* GetNPNIface()
+ {
+ return mNPNIface;
+ }
+
+ bool
+ RegisterNPObjectForActor(NPObject* aObject,
+ PluginScriptableObjectParent* aActor);
+
+ void
+ UnregisterNPObject(NPObject* aObject);
+
+ PluginScriptableObjectParent*
+ GetActorForNPObject(NPObject* aObject);
+
+ NPP
+ GetNPP()
+ {
+ return mNPP;
+ }
+
+ virtual bool
+ AnswerPluginGotFocus();
+
+ virtual bool
+ RecvSetNestedEventState(const bool& aState);
+
+private:
+ bool InternalGetValueForNPObject(NPNVariable aVariable,
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult);
+
+private:
+ PluginModuleParent* mParent;
+ NPP mNPP;
+ const NPNetscapeFuncs* mNPNIface;
+ NPWindowType mWindowType;
+
+ nsDataHashtable<nsVoidPtrHashKey, PluginScriptableObjectParent*> mScriptableObjects;
+
+#if defined(OS_WIN)
+private:
+ // Used in rendering windowless plugins in other processes.
+ bool SharedSurfaceSetWindow(const NPWindow* aWindow, NPRemoteWindow& aRemoteWindow);
+ void SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent);
+ void SharedSurfaceAfterPaint(NPEvent* npevent);
+ void SharedSurfaceRelease();
+ // Used in handling parent/child forwarding of events.
+ static LRESULT CALLBACK PluginWindowHookProc(HWND hWnd, UINT message,
+ WPARAM wParam, LPARAM lParam);
+ void SubclassPluginWindow(HWND aWnd);
+ void UnsubclassPluginWindow();
+
+private:
+ gfx::SharedDIBWin mSharedSurfaceDib;
+ nsIntRect mPluginPort;
+ nsIntRect mSharedSize;
+ HWND mPluginHWND;
+ WNDPROC mPluginWndProc;
+ bool mNestedEventState;
+#endif // defined(XP_WIN)
+};
+
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_PluginInstanceParent_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginLibrary.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Foundation.
+ *
+ * The Initial Developer of the Original Code is
+ * Josh Aas <josh@mozilla.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the pro