mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
c2b7d7ae22
@ -22,6 +22,7 @@ addons = [
|
||||
'child_process',
|
||||
'chrome',
|
||||
'content-permissions',
|
||||
'content-script-messages-latency',
|
||||
'contributors',
|
||||
'curly-id',
|
||||
'developers',
|
||||
|
@ -51,9 +51,19 @@ const WorkerChild = Class({
|
||||
|
||||
this.sandbox = WorkerSandbox(this, this.window);
|
||||
|
||||
// If the document is still loading wait for it to finish before passing on
|
||||
// received messages
|
||||
this.frozen = this.window.document.readyState == "loading";
|
||||
// If the document has an unexpected readyState, its worker-child instance is initialized
|
||||
// as frozen until one of the known readyState is reached.
|
||||
let initialDocumentReadyState = this.window.document.readyState;
|
||||
this.frozen = [
|
||||
"loading", "interactive", "complete"
|
||||
].includes(initialDocumentReadyState) ? false : true;
|
||||
|
||||
if (this.frozen) {
|
||||
console.warn("SDK worker-child started as frozen on unexpected initial document.readyState", {
|
||||
initialDocumentReadyState, windowLocation: this.window.location.href,
|
||||
});
|
||||
}
|
||||
|
||||
this.frozenMessages = [];
|
||||
this.on('pageshow', () => {
|
||||
this.frozen = false;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,90 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { PageMod } = require("sdk/page-mod");
|
||||
const tabs = require("sdk/tabs");
|
||||
const { startServerAsync } = require("./httpd");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
|
||||
const serverPort = 8099;
|
||||
|
||||
exports.testContentScriptLatencyRegression = function*(assert) {
|
||||
let server = startServerAsync(serverPort);
|
||||
server.registerPathHandler("/", function handle(request, response) {
|
||||
response.write(`<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/slow.css">
|
||||
</head>
|
||||
<body>
|
||||
slow loading page...
|
||||
</body>
|
||||
</html>`);
|
||||
});
|
||||
|
||||
server.registerPathHandler("/slow.css", function handle(request, response) {
|
||||
response.processAsync();
|
||||
response.setHeader('Content-Type', 'text/css', false);
|
||||
setTimeout(_ => {
|
||||
response.write("body { background: red; }");
|
||||
response.finish();
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
|
||||
let pageMod;
|
||||
|
||||
let worker = yield new Promise((resolve) => {
|
||||
pageMod = PageMod({
|
||||
include: "http://localhost:8099/",
|
||||
attachTo: "top",
|
||||
contentScriptWhen: "start",
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
self.port.on("a-port-message", function () {
|
||||
self.port.emit("document-ready-state", document.readyState);
|
||||
});
|
||||
},
|
||||
onAttach: function(w) {
|
||||
resolve(w);
|
||||
}
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
url: "http://localhost:8099/",
|
||||
inBackground: true
|
||||
});
|
||||
});
|
||||
|
||||
worker.port.emit("a-port-message");
|
||||
|
||||
let waitForPortMessage = new Promise((resolve) => {
|
||||
worker.port.once("document-ready-state", (msg) => {
|
||||
resolve(msg);
|
||||
});
|
||||
});
|
||||
|
||||
let documentReadyState = yield waitForPortMessage;
|
||||
|
||||
assert.notEqual(
|
||||
"complete", documentReadyState,
|
||||
"content script received the port message when the page was still loading"
|
||||
);
|
||||
|
||||
assert.notEqual(
|
||||
"uninitialized", documentReadyState,
|
||||
"content script should be frozen if document.readyState is still uninitialized"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
["loading", "interactive"].includes(documentReadyState),
|
||||
"content script message received with document.readyState was interactive or loading"
|
||||
);
|
||||
|
||||
// Cleanup.
|
||||
pageMod.destroy();
|
||||
yield new Promise((resolve) => worker.tab.close(resolve));
|
||||
yield new Promise((resolve) => server.stop(resolve));
|
||||
};
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
@ -0,0 +1,6 @@
|
||||
|
||||
{
|
||||
"id": "content-script-messages-latency@jetpack",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
[child_process.xpi]
|
||||
[chrome.xpi]
|
||||
[content-permissions.xpi]
|
||||
[content-script-messages-latency.xpi]
|
||||
[contributors.xpi]
|
||||
[curly-id.xpi]
|
||||
[developers.xpi]
|
||||
|
@ -873,7 +873,7 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#navigator-toolbox:not(:hover) #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
|
||||
#navigator-toolbox:not(:hover) #nav-bar:not([customizing="true"]) #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -1608,7 +1608,7 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#navigator-toolbox:not(:hover) #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
|
||||
#navigator-toolbox:not(:hover) #nav-bar:not([customizing="true"]) #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -1360,7 +1360,7 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#navigator-toolbox:not(:hover) #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
|
||||
#navigator-toolbox:not(:hover) #nav-bar:not([customizing="true"]) #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ define(function (require, exports, module) {
|
||||
},
|
||||
|
||||
getTitle: function (grip) {
|
||||
return new Date(grip.preview.timestamp).toString();
|
||||
return new Date(grip.preview.timestamp).toISOString();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
|
@ -6,6 +6,7 @@ support-files =
|
||||
[test_notification_box_01.html]
|
||||
[test_notification_box_02.html]
|
||||
[test_notification_box_03.html]
|
||||
[test_reps_date-time.html]
|
||||
[test_reps_object-with-url.html]
|
||||
[test_reps_undefined.html]
|
||||
[test_reps_window.html]
|
||||
|
@ -0,0 +1,51 @@
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test DateTime rep
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Rep test - DateTime</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
|
||||
let { DateTime } = browserRequire("devtools/client/shared/components/reps/date-time");
|
||||
|
||||
let gripStub = {
|
||||
"type": "object",
|
||||
"class": "Date",
|
||||
"actor": "server1.conn0.child1/obj32",
|
||||
"extensible": true,
|
||||
"frozen": false,
|
||||
"sealed": false,
|
||||
"ownPropertyLength": 0,
|
||||
"preview": {
|
||||
"timestamp": 1459372644859
|
||||
}
|
||||
};
|
||||
|
||||
// Test that correct rep is chosen
|
||||
const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
|
||||
is(renderedRep.type, DateTime.rep, `Rep correctly selects ${DateTime.rep.displayName}`);
|
||||
|
||||
// Test rendering
|
||||
const renderedComponent = renderComponent(DateTime.rep, { object: gripStub });
|
||||
is(renderedComponent.textContent, "2016-03-30T21:17:24.859Z", "DateTime rep has expected text content");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -34,7 +34,7 @@
|
||||
color: #fff;
|
||||
border: 1px solid #111;
|
||||
border-radius: 2px;
|
||||
padding: 5px;
|
||||
padding: 0 5px;
|
||||
width: 200px;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ public class SiteIdentity {
|
||||
private final String LOGTAG = "GeckoSiteIdentity";
|
||||
private SecurityMode mSecurityMode;
|
||||
private boolean mSecure;
|
||||
private boolean mLoginInsecure;
|
||||
private MixedMode mMixedModeActive;
|
||||
private MixedMode mMixedModeDisplay;
|
||||
private TrackingMode mTrackingMode;
|
||||
@ -137,7 +136,6 @@ public class SiteIdentity {
|
||||
mCountry = null;
|
||||
mVerifier = null;
|
||||
mSecure = false;
|
||||
mLoginInsecure = false;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@ -237,14 +235,6 @@ public class SiteIdentity {
|
||||
return mSecure;
|
||||
}
|
||||
|
||||
public void setLoginInsecure(boolean isInsecure) {
|
||||
mLoginInsecure = isInsecure;
|
||||
}
|
||||
|
||||
public boolean loginInsecure() {
|
||||
return mLoginInsecure;
|
||||
}
|
||||
|
||||
public MixedMode getMixedModeActive() {
|
||||
return mMixedModeActive;
|
||||
}
|
||||
|
@ -540,10 +540,6 @@ public class Tab {
|
||||
mSiteIdentity.update(identityData);
|
||||
}
|
||||
|
||||
public void setLoginInsecure(boolean isInsecure) {
|
||||
mSiteIdentity.setLoginInsecure(isInsecure);
|
||||
}
|
||||
|
||||
public void setSiteLogins(SiteLogins siteLogins) {
|
||||
mSiteLogins = siteLogins;
|
||||
}
|
||||
|
@ -106,7 +106,6 @@ public class Tabs implements GeckoEventListener {
|
||||
"Tab:Close",
|
||||
"Tab:Select",
|
||||
"Content:LocationChange",
|
||||
"Content:LoginInsecure",
|
||||
"Content:SecurityChange",
|
||||
"Content:StateChange",
|
||||
"Content:LoadError",
|
||||
@ -490,9 +489,6 @@ public class Tabs implements GeckoEventListener {
|
||||
} else if (event.equals("Content:SecurityChange")) {
|
||||
tab.updateIdentityData(message.getJSONObject("identity"));
|
||||
notifyListeners(tab, TabEvents.SECURITY_CHANGE);
|
||||
} else if (event.equals("Content:LoginInsecure")) {
|
||||
tab.setLoginInsecure(true);
|
||||
notifyListeners(tab, TabEvents.SECURITY_CHANGE);
|
||||
} else if (event.equals("Content:StateChange")) {
|
||||
int state = message.getInt("state");
|
||||
if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
|
||||
|
@ -323,14 +323,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
|
||||
mMixedContentActivity.setVisibility(View.GONE);
|
||||
mLink.setVisibility(View.GONE);
|
||||
} else if (!siteIdentity.isSecure()) {
|
||||
if (siteIdentity.loginInsecure()) {
|
||||
// Login detected on an insecure page.
|
||||
mIcon.setImageResource(R.drawable.lock_disabled);
|
||||
clearSecurityStateIcon();
|
||||
|
||||
mMixedContentActivity.setVisibility(View.VISIBLE);
|
||||
mMixedContentActivity.setText(R.string.identity_login_insecure);
|
||||
} else if (siteIdentity.getMixedModeActive() == MixedMode.MIXED_CONTENT_LOADED) {
|
||||
if (siteIdentity.getMixedModeActive() == MixedMode.MIXED_CONTENT_LOADED) {
|
||||
// Active Mixed Content loaded because user has disabled blocking.
|
||||
mIcon.setImageResource(R.drawable.lock_disabled);
|
||||
clearSecurityStateIcon();
|
||||
|
@ -350,19 +350,16 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout {
|
||||
final MixedMode activeMixedMode;
|
||||
final MixedMode displayMixedMode;
|
||||
final TrackingMode trackingMode;
|
||||
final boolean loginInsecure;
|
||||
if (siteIdentity == null) {
|
||||
securityMode = SecurityMode.UNKNOWN;
|
||||
activeMixedMode = MixedMode.UNKNOWN;
|
||||
displayMixedMode = MixedMode.UNKNOWN;
|
||||
trackingMode = TrackingMode.UNKNOWN;
|
||||
loginInsecure = false;
|
||||
} else {
|
||||
securityMode = siteIdentity.getSecurityMode();
|
||||
activeMixedMode = siteIdentity.getMixedModeActive();
|
||||
displayMixedMode = siteIdentity.getMixedModeDisplay();
|
||||
trackingMode = siteIdentity.getTrackingMode();
|
||||
loginInsecure = siteIdentity.loginInsecure();
|
||||
}
|
||||
|
||||
// This is a bit tricky, but we have one icon and three potential indicators.
|
||||
@ -381,8 +378,6 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout {
|
||||
if (AboutPages.isTitlelessAboutPage(tab.getURL())) {
|
||||
// We always want to just show a search icon on about:home
|
||||
imageLevel = LEVEL_SEARCH_ICON;
|
||||
} else if (loginInsecure) {
|
||||
imageLevel = LEVEL_LOCK_DISABLED;
|
||||
} else if (trackingMode == TrackingMode.TRACKING_CONTENT_LOADED) {
|
||||
imageLevel = LEVEL_SHIELD_DISABLED;
|
||||
} else if (trackingMode == TrackingMode.TRACKING_CONTENT_BLOCKED) {
|
||||
|
@ -627,7 +627,6 @@ with that structure, consider a translation which ignores the preceding domain a
|
||||
just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY identity_connection_secure "Secure Connection">
|
||||
<!ENTITY identity_connection_insecure "Insecure connection">
|
||||
<!ENTITY identity_login_insecure "This page is not secure and your login could be vulnerable.">
|
||||
<!ENTITY identity_connection_chromeui "This is a secure &brandShortName; page">
|
||||
|
||||
<!-- Mixed content notifications in site identity popup -->
|
||||
|
@ -495,7 +495,6 @@
|
||||
<!-- Site identity popup -->
|
||||
<string name="identity_connection_secure">&identity_connection_secure;</string>
|
||||
<string name="identity_connection_insecure">&identity_connection_insecure;</string>
|
||||
<string name="identity_login_insecure">&identity_login_insecure;</string>
|
||||
<string name="identity_connection_chromeui">&identity_connection_chromeui;</string>
|
||||
|
||||
<string name="mixed_content_blocked_all">&mixed_content_blocked_all1;</string>
|
||||
|
@ -4671,9 +4671,6 @@ var BrowserEventHandler = {
|
||||
|
||||
InitLater(() => BrowserApp.deck.addEventListener("click", InputWidgetHelper, true));
|
||||
InitLater(() => BrowserApp.deck.addEventListener("click", SelectHelper, true));
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
InitLater(() => BrowserApp.deck.addEventListener("InsecureLoginFormsStateChange", IdentityHandler.sendLoginInsecure, true));
|
||||
}
|
||||
|
||||
// ReaderViews support backPress listeners.
|
||||
Messaging.addListener(() => {
|
||||
@ -6382,18 +6379,6 @@ var IdentityHandler = {
|
||||
return this.TRACKING_MODE_UNKNOWN;
|
||||
},
|
||||
|
||||
sendLoginInsecure: function sendLoginInsecure() {
|
||||
let loginInsecure = LoginManagerParent.hasInsecureLoginForms(BrowserApp.selectedBrowser);
|
||||
if (loginInsecure) {
|
||||
let message = {
|
||||
type: "Content:LoginInsecure",
|
||||
tabID: BrowserApp.selectedTab.id
|
||||
};
|
||||
Messaging.sendRequest(message);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
shieldHistogramAdd: function(browser, value) {
|
||||
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
|
||||
return;
|
||||
|
@ -974,11 +974,16 @@ LoginManagerPrompter.prototype = {
|
||||
toggleBtn.setAttribute("accesskey", togglePasswordAccessKey);
|
||||
toggleBtn.setAttribute("hidden", LoginHelper.isMasterPasswordSet());
|
||||
}
|
||||
if (this.wasDismissed) {
|
||||
chromeDoc.getElementById("password-notification-visibilityToggle")
|
||||
.setAttribute("hidden", true);
|
||||
}
|
||||
break;
|
||||
case "shown":
|
||||
writeDataToUI();
|
||||
break;
|
||||
case "dismissed":
|
||||
this.wasDismissed = true;
|
||||
readDataFromUI();
|
||||
// Fall through.
|
||||
case "removed":
|
||||
|
@ -553,5 +553,28 @@ add_task(function* test_recipeCaptureFields_ExistingLogin() {
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
add_task(function* test_noShowPasswordOnDismissal() {
|
||||
info("Check for no Show Password field when the doorhanger is dismissed");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
info("Opening popup");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
let { panel } = PopupNotifications;
|
||||
|
||||
info("Hiding popup.");
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
|
||||
panel.hidePopup();
|
||||
yield promiseHidden;
|
||||
|
||||
info("Clicking on anchor to reshow popup.")
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(panel, "popupshown");
|
||||
notif.anchorElement.click();
|
||||
yield promiseShown;
|
||||
|
||||
let passwordVisiblityToggle = panel.querySelector("#password-notification-visibilityToggle");
|
||||
is(passwordVisiblityToggle.hidden, true, "Check that the Show Password field is Hidden");
|
||||
});
|
||||
});
|
||||
|
||||
// TODO:
|
||||
// * existing login test, form has different password --> change password, no save prompt
|
||||
|
@ -287,6 +287,7 @@ SaveToEnv(const char *putenv)
|
||||
if (expr)
|
||||
PR_SetEnv(expr);
|
||||
// We intentionally leak |expr| here since it is required by PR_SetEnv.
|
||||
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(expr);
|
||||
}
|
||||
|
||||
// Tests that an environment variable exists and has a value
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -33,7 +34,12 @@
|
||||
|
||||
#ifdef MOZ_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/ICE/ICElib.h>
|
||||
#include <X11/SM/SMlib.h>
|
||||
#include <fcntl.h>
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
@ -46,95 +52,399 @@
|
||||
You have GTK+ %d.%d.\nThis application requires GTK+ %d.%d or newer.\n\n\
|
||||
Please upgrade your GTK+ library if you wish to use this application."
|
||||
|
||||
typedef struct _GnomeProgram GnomeProgram;
|
||||
typedef struct _GnomeModuleInfo GnomeModuleInfo;
|
||||
typedef struct _GnomeClient GnomeClient;
|
||||
#if MOZ_X11
|
||||
#undef IceSetIOErrorHandler
|
||||
#undef IceAddConnectionWatch
|
||||
#undef IceConnectionNumber
|
||||
#undef IceProcessMessages
|
||||
#undef IceGetConnectionContext
|
||||
#undef SmcInteractDone
|
||||
#undef SmcSaveYourselfDone
|
||||
#undef SmcInteractRequest
|
||||
#undef SmcCloseConnection
|
||||
#undef SmcOpenConnection
|
||||
#undef SmcSetProperties
|
||||
|
||||
typedef enum {
|
||||
GNOME_SAVE_GLOBAL,
|
||||
GNOME_SAVE_LOCAL,
|
||||
GNOME_SAVE_BOTH
|
||||
} GnomeSaveStyle;
|
||||
typedef IceIOErrorHandler (*IceSetIOErrorHandlerFn) (IceIOErrorHandler);
|
||||
typedef int (*IceAddConnectionWatchFn) (IceWatchProc, IcePointer);
|
||||
typedef int (*IceConnectionNumberFn) (IceConn);
|
||||
typedef IceProcessMessagesStatus (*IceProcessMessagesFn) (IceConn, IceReplyWaitInfo*, Bool*);
|
||||
typedef IcePointer (*IceGetConnectionContextFn) (IceConn);
|
||||
|
||||
typedef enum {
|
||||
GNOME_INTERACT_NONE,
|
||||
GNOME_INTERACT_ERRORS,
|
||||
GNOME_INTERACT_ANY
|
||||
} GnomeInteractStyle;
|
||||
typedef void (*SmcInteractDoneFn) (SmcConn, Bool);
|
||||
typedef void (*SmcSaveYourselfDoneFn) (SmcConn, Bool);
|
||||
typedef int (*SmcInteractRequestFn) (SmcConn, int, SmcInteractProc, SmPointer);
|
||||
typedef SmcCloseStatus (*SmcCloseConnectionFn) (SmcConn, int, char**);
|
||||
typedef SmcConn (*SmcOpenConnectionFn) (char*, SmPointer, int, int,
|
||||
unsigned long, SmcCallbacks*,
|
||||
const char*, char**, int, char*);
|
||||
typedef void (*SmcSetPropertiesFn) (SmcConn, int, SmProp**);
|
||||
|
||||
typedef enum {
|
||||
GNOME_DIALOG_ERROR,
|
||||
GNOME_DIALOG_NORMAL
|
||||
} GnomeDialogType;
|
||||
static IceSetIOErrorHandlerFn IceSetIOErrorHandlerPtr;
|
||||
static IceAddConnectionWatchFn IceAddConnectionWatchPtr;
|
||||
static IceConnectionNumberFn IceConnectionNumberPtr;
|
||||
static IceProcessMessagesFn IceProcessMessagesPtr;
|
||||
static IceGetConnectionContextFn IceGetConnectionContextPtr;
|
||||
static SmcInteractDoneFn SmcInteractDonePtr;
|
||||
static SmcSaveYourselfDoneFn SmcSaveYourselfDonePtr;
|
||||
static SmcInteractRequestFn SmcInteractRequestPtr;
|
||||
static SmcCloseConnectionFn SmcCloseConnectionPtr;
|
||||
static SmcOpenConnectionFn SmcOpenConnectionPtr;
|
||||
static SmcSetPropertiesFn SmcSetPropertiesPtr;
|
||||
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
typedef GnomeProgram * (*_gnome_program_init_fn)(const char *, const char *,
|
||||
const GnomeModuleInfo *, int,
|
||||
char **, const char *, ...);
|
||||
typedef GnomeProgram * (*_gnome_program_get_fn)(void);
|
||||
typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)();
|
||||
typedef GnomeClient * (*_gnome_master_client_fn)(void);
|
||||
typedef void (*_gnome_client_set_restart_command_fn)(GnomeClient*, gint, gchar*[]);
|
||||
#define IceSetIOErrorHandler IceSetIOErrorHandlerPtr
|
||||
#define IceAddConnectionWatch IceAddConnectionWatchPtr
|
||||
#define IceConnectionNumber IceConnectionNumberPtr
|
||||
#define IceProcessMessages IceProcessMessagesPtr
|
||||
#define IceGetConnectionContext IceGetConnectionContextPtr
|
||||
#define SmcInteractDone SmcInteractDonePtr
|
||||
#define SmcSaveYourselfDone SmcSaveYourselfDonePtr
|
||||
#define SmcInteractRequest SmcInteractRequestPtr
|
||||
#define SmcCloseConnection SmcCloseConnectionPtr
|
||||
#define SmcOpenConnection SmcOpenConnectionPtr
|
||||
#define SmcSetProperties SmcSetPropertiesPtr
|
||||
|
||||
static _gnome_client_set_restart_command_fn gnome_client_set_restart_command;
|
||||
#endif
|
||||
enum ClientState {
|
||||
STATE_DISCONNECTED,
|
||||
STATE_REGISTERING,
|
||||
STATE_IDLE,
|
||||
STATE_INTERACTING,
|
||||
STATE_SHUTDOWN_CANCELLED
|
||||
};
|
||||
|
||||
gboolean save_yourself_cb(GnomeClient *client, gint phase,
|
||||
GnomeSaveStyle style, gboolean shutdown,
|
||||
GnomeInteractStyle interact, gboolean fast,
|
||||
gpointer user_data)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
static const char *gClientStateTable[] = {
|
||||
"DISCONNECTED",
|
||||
"REGISTERING",
|
||||
"IDLE",
|
||||
"INTERACTING",
|
||||
"SHUTDOWN_CANCELLED"
|
||||
};
|
||||
|
||||
nsCOMPtr<nsISupportsPRBool> didSaveSession =
|
||||
do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
if (!obsServ || !didSaveSession)
|
||||
return TRUE; // OOM
|
||||
|
||||
// Notify observers to save the session state
|
||||
didSaveSession->SetData(false);
|
||||
obsServ->NotifyObservers(didSaveSession, "session-save", nullptr);
|
||||
|
||||
bool status;
|
||||
didSaveSession->GetData(&status);
|
||||
|
||||
// If there was no session saved and the save_yourself request is
|
||||
// caused by upcoming shutdown we like to prepare for it
|
||||
if (!status && shutdown) {
|
||||
nsCOMPtr<nsISupportsPRBool> cancelQuit =
|
||||
do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
cancelQuit->SetData(false);
|
||||
obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
|
||||
|
||||
bool abortQuit;
|
||||
cancelQuit->GetData(&abortQuit);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void die_cb(GnomeClient *client, gpointer user_data)
|
||||
{
|
||||
nsCOMPtr<nsIAppStartup> appService =
|
||||
do_GetService("@mozilla.org/toolkit/app-startup;1");
|
||||
|
||||
if (appService)
|
||||
appService->Quit(nsIAppStartup::eForceQuit);
|
||||
}
|
||||
static LazyLogModule sMozSMLog("MozSM");
|
||||
#endif /* MOZ_X11 */
|
||||
|
||||
class nsNativeAppSupportUnix : public nsNativeAppSupportBase
|
||||
{
|
||||
public:
|
||||
#if MOZ_X11
|
||||
nsNativeAppSupportUnix(): mSessionConnection(nullptr),
|
||||
mClientState(STATE_DISCONNECTED) {};
|
||||
~nsNativeAppSupportUnix()
|
||||
{
|
||||
// this goes out of scope after "web-workers-shutdown" async shutdown phase
|
||||
// so it's safe to disconnect here (i.e. the application won't lose data)
|
||||
DisconnectFromSM();
|
||||
};
|
||||
|
||||
void DisconnectFromSM();
|
||||
#endif
|
||||
NS_IMETHOD Start(bool* aRetVal);
|
||||
NS_IMETHOD Stop(bool *aResult);
|
||||
NS_IMETHOD Enable();
|
||||
|
||||
private:
|
||||
#if MOZ_X11
|
||||
static void SaveYourselfCB(SmcConn smc_conn, SmPointer client_data,
|
||||
int save_style, Bool shutdown, int interact_style,
|
||||
Bool fast);
|
||||
static void DieCB(SmcConn smc_conn, SmPointer client_data);
|
||||
static void InteractCB(SmcConn smc_conn, SmPointer client_data);
|
||||
static void SaveCompleteCB(SmcConn smc_conn, SmPointer client_data) {};
|
||||
static void ShutdownCancelledCB(SmcConn smc_conn, SmPointer client_data);
|
||||
void DoInteract();
|
||||
void SetClientState(ClientState aState)
|
||||
{
|
||||
mClientState = aState;
|
||||
MOZ_LOG(sMozSMLog, LogLevel::Debug, ("New state = %s\n", gClientStateTable[aState]));
|
||||
}
|
||||
|
||||
SmcConn mSessionConnection;
|
||||
ClientState mClientState;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if MOZ_X11
|
||||
static gboolean
|
||||
process_ice_messages(IceConn connection)
|
||||
{
|
||||
IceProcessMessagesStatus status;
|
||||
|
||||
status = IceProcessMessages(connection, nullptr, nullptr);
|
||||
|
||||
switch (status) {
|
||||
case IceProcessMessagesSuccess:
|
||||
return TRUE;
|
||||
|
||||
case IceProcessMessagesIOError: {
|
||||
nsNativeAppSupportUnix *native =
|
||||
static_cast<nsNativeAppSupportUnix *>(IceGetConnectionContext(connection));
|
||||
native->DisconnectFromSM();
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
case IceProcessMessagesConnectionClosed:
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ice_iochannel_watch(GIOChannel *channel, GIOCondition condition,
|
||||
gpointer client_data)
|
||||
{
|
||||
return process_ice_messages(static_cast<IceConn>(client_data));
|
||||
}
|
||||
|
||||
static void
|
||||
ice_connection_watch(IceConn connection, IcePointer client_data,
|
||||
Bool opening, IcePointer *watch_data)
|
||||
{
|
||||
guint watch_id;
|
||||
|
||||
if (opening) {
|
||||
GIOChannel *channel;
|
||||
int fd = IceConnectionNumber(connection);
|
||||
|
||||
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
||||
channel = g_io_channel_unix_new(fd);
|
||||
watch_id = g_io_add_watch(channel,
|
||||
static_cast<GIOCondition>(G_IO_IN | G_IO_ERR),
|
||||
ice_iochannel_watch, connection);
|
||||
g_io_channel_unref(channel);
|
||||
|
||||
*watch_data = GUINT_TO_POINTER(watch_id);
|
||||
} else {
|
||||
watch_id = GPOINTER_TO_UINT(*watch_data);
|
||||
g_source_remove(watch_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ice_io_error_handler(IceConn connection)
|
||||
{
|
||||
// override the default handler which would exit the application;
|
||||
// do nothing and let ICELib handle the failure of the connection gracefully.
|
||||
}
|
||||
|
||||
static void
|
||||
ice_init(void)
|
||||
{
|
||||
static bool initted = false;
|
||||
|
||||
if (!initted) {
|
||||
IceSetIOErrorHandler(ice_io_error_handler);
|
||||
IceAddConnectionWatch(ice_connection_watch, nullptr);
|
||||
initted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeAppSupportUnix::InteractCB(SmcConn smc_conn, SmPointer client_data)
|
||||
{
|
||||
nsNativeAppSupportUnix *self =
|
||||
static_cast<nsNativeAppSupportUnix *>(client_data);
|
||||
|
||||
self->SetClientState(STATE_INTERACTING);
|
||||
|
||||
// We do this asynchronously, as we spin the event loop recursively if
|
||||
// a dialog is displayed. If we do this synchronously, we don't finish
|
||||
// processing the current ICE event whilst the dialog is displayed, which
|
||||
// means we won't process any more. libsm hates us if we do the InteractDone
|
||||
// with a pending ShutdownCancelled, and we would certainly like to handle Die
|
||||
// whilst a dialog is displayed
|
||||
NS_DispatchToCurrentThread(NewRunnableMethod(self, &nsNativeAppSupportUnix::DoInteract));
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeAppSupportUnix::DoInteract()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
if (!obsServ) {
|
||||
SmcInteractDone(mSessionConnection, False);
|
||||
SmcSaveYourselfDone(mSessionConnection, True);
|
||||
SetClientState(STATE_IDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPRBool> cancelQuit =
|
||||
do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
bool abortQuit = false;
|
||||
if (cancelQuit) {
|
||||
cancelQuit->SetData(false);
|
||||
obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
|
||||
|
||||
cancelQuit->GetData(&abortQuit);
|
||||
}
|
||||
|
||||
if (!abortQuit && mClientState == STATE_DISCONNECTED) {
|
||||
// The session manager disappeared, whilst we were interacting, so
|
||||
// quit now
|
||||
nsCOMPtr<nsIAppStartup> appService =
|
||||
do_GetService("@mozilla.org/toolkit/app-startup;1");
|
||||
|
||||
if (appService) {
|
||||
appService->Quit(nsIAppStartup::eForceQuit);
|
||||
}
|
||||
} else {
|
||||
if (mClientState != STATE_SHUTDOWN_CANCELLED) {
|
||||
// Only do this if the shutdown wasn't cancelled
|
||||
SmcInteractDone(mSessionConnection, !!abortQuit);
|
||||
SmcSaveYourselfDone(mSessionConnection, !abortQuit);
|
||||
}
|
||||
|
||||
SetClientState(STATE_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeAppSupportUnix::SaveYourselfCB(SmcConn smc_conn, SmPointer client_data,
|
||||
int save_style, Bool shutdown,
|
||||
int interact_style, Bool fast)
|
||||
{
|
||||
nsNativeAppSupportUnix *self =
|
||||
static_cast<nsNativeAppSupportUnix *>(client_data);
|
||||
|
||||
// Expect a SaveYourselfCB if we're registering a new client.
|
||||
// All properties are already set in Start() so just reply with
|
||||
// SmcSaveYourselfDone if the callback matches the expected signature.
|
||||
//
|
||||
// Ancient versions (?) of xsm do not follow such an early SaveYourself with
|
||||
// SaveComplete. This is a problem if the application freezes interaction
|
||||
// while waiting for a response to SmcSaveYourselfDone. So never freeze
|
||||
// interaction when in STATE_REGISTERING.
|
||||
//
|
||||
// That aside, we could treat each combination of flags appropriately and not
|
||||
// special-case this.
|
||||
if (self->mClientState == STATE_REGISTERING) {
|
||||
self->SetClientState(STATE_IDLE);
|
||||
|
||||
if (save_style == SmSaveLocal && interact_style == SmInteractStyleNone &&
|
||||
!shutdown && !fast) {
|
||||
SmcSaveYourselfDone(self->mSessionConnection, True);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->mClientState == STATE_SHUTDOWN_CANCELLED) {
|
||||
// The last shutdown request was cancelled whilst we were interacting,
|
||||
// and we haven't finished interacting yet. Switch the state back again
|
||||
self->SetClientState(STATE_INTERACTING);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
if (!obsServ) {
|
||||
SmcSaveYourselfDone(smc_conn, True);
|
||||
return;
|
||||
}
|
||||
|
||||
bool status = false;
|
||||
if (save_style != SmSaveGlobal) {
|
||||
nsCOMPtr<nsISupportsPRBool> didSaveSession =
|
||||
do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
if (!didSaveSession) {
|
||||
SmcSaveYourselfDone(smc_conn, True);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify observers to save the session state
|
||||
didSaveSession->SetData(false);
|
||||
obsServ->NotifyObservers(didSaveSession, "session-save", nullptr);
|
||||
|
||||
didSaveSession->GetData(&status);
|
||||
}
|
||||
|
||||
// If the interact style permits us to, we are shutting down and we didn't
|
||||
// manage to (or weren't asked to) save the local state, then notify the user
|
||||
// in advance that we are doing to quit (assuming that we aren't already
|
||||
// doing so)
|
||||
if (!status && shutdown && interact_style != SmInteractStyleNone) {
|
||||
if (self->mClientState != STATE_INTERACTING) {
|
||||
SmcInteractRequest(smc_conn, SmDialogNormal,
|
||||
nsNativeAppSupportUnix::InteractCB, client_data);
|
||||
}
|
||||
} else {
|
||||
SmcSaveYourselfDone(smc_conn, True);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeAppSupportUnix::DieCB(SmcConn smc_conn, SmPointer client_data)
|
||||
{
|
||||
nsCOMPtr<nsIAppStartup> appService =
|
||||
do_GetService("@mozilla.org/toolkit/app-startup;1");
|
||||
|
||||
if (appService) {
|
||||
appService->Quit(nsIAppStartup::eForceQuit);
|
||||
}
|
||||
// Quit causes the shutdown to begin but the shutdown process is asynchronous
|
||||
// so we can't DisconnectFromSM() yet
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeAppSupportUnix::ShutdownCancelledCB(SmcConn smc_conn,
|
||||
SmPointer client_data)
|
||||
{
|
||||
nsNativeAppSupportUnix *self =
|
||||
static_cast<nsNativeAppSupportUnix *>(client_data);
|
||||
|
||||
// Interacting is the only time when we wouldn't already have called
|
||||
// SmcSaveYourselfDone. Do that now, then set the state to make sure we
|
||||
// don't send it again after finishing interacting
|
||||
if (self->mClientState == STATE_INTERACTING) {
|
||||
SmcSaveYourselfDone(smc_conn, False);
|
||||
self->SetClientState(STATE_SHUTDOWN_CANCELLED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeAppSupportUnix::DisconnectFromSM()
|
||||
{
|
||||
// the SM is free to exit any time after we disconnect, so callers must be
|
||||
// sure to have reached a sufficiently advanced phase of shutdown that there
|
||||
// is no risk of data loss:
|
||||
// e.g. all async writes are complete by the end of "profile-before-change"
|
||||
if (mSessionConnection) {
|
||||
SetClientState(STATE_DISCONNECTED);
|
||||
SmcCloseConnection(mSessionConnection, 0, nullptr);
|
||||
mSessionConnection = nullptr;
|
||||
gdk_x11_set_sm_client_id(nullptr); // follow gnome-client behaviour
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetSMValue(SmPropValue& val, const nsCString& data)
|
||||
{
|
||||
val.value = static_cast<SmPointer>(const_cast<char*>(data.get()));
|
||||
val.length = data.Length();
|
||||
}
|
||||
|
||||
static void
|
||||
SetSMProperty(SmProp& prop, const char* name, const char* type, int numVals,
|
||||
SmPropValue vals[])
|
||||
{
|
||||
prop.name = const_cast<char*>(name);
|
||||
prop.type = const_cast<char*>(type);
|
||||
prop.num_vals = numVals;
|
||||
prop.vals = vals;
|
||||
}
|
||||
#endif /* MOZ_X11 */
|
||||
|
||||
static void RemoveArg(char **argv)
|
||||
{
|
||||
do {
|
||||
*argv = *(argv + 1);
|
||||
++argv;
|
||||
} while (*argv);
|
||||
|
||||
--gArgc;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNativeAppSupportUnix::Start(bool *aRetVal)
|
||||
{
|
||||
@ -168,79 +478,132 @@ nsNativeAppSupportUnix::Start(bool *aRetVal)
|
||||
|
||||
*aRetVal = true;
|
||||
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
#ifdef MOZ_X11
|
||||
gboolean sm_disable = FALSE;
|
||||
if (!getenv("SESSION_MANAGER")) {
|
||||
sm_disable = TRUE;
|
||||
}
|
||||
|
||||
PRLibrary *gnomeuiLib = PR_LoadLibrary("libgnomeui-2.so.0");
|
||||
if (!gnomeuiLib)
|
||||
return NS_OK;
|
||||
nsAutoCString prev_client_id;
|
||||
|
||||
PRLibrary *gnomeLib = PR_LoadLibrary("libgnome-2.so.0");
|
||||
if (!gnomeLib) {
|
||||
PR_UnloadLibrary(gnomeuiLib);
|
||||
char **curarg = gArgv + 1;
|
||||
while (*curarg) {
|
||||
char *arg = *curarg;
|
||||
if (arg[0] == '-' && arg[1] == '-') {
|
||||
arg += 2;
|
||||
if (!strcmp(arg, "sm-disable")) {
|
||||
RemoveArg(curarg);
|
||||
sm_disable = TRUE;
|
||||
continue;
|
||||
} else if (!strcmp(arg, "sm-client-id")) {
|
||||
RemoveArg(curarg);
|
||||
if (*curarg[0] != '-') {
|
||||
prev_client_id = *curarg;
|
||||
RemoveArg(curarg);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++curarg;
|
||||
}
|
||||
|
||||
if (prev_client_id.IsEmpty()) {
|
||||
prev_client_id = getenv("DESKTOP_AUTOSTART_ID");
|
||||
}
|
||||
|
||||
// We don't want child processes to use the same ID
|
||||
unsetenv("DESKTOP_AUTOSTART_ID");
|
||||
|
||||
char *client_id = nullptr;
|
||||
if (!sm_disable) {
|
||||
PRLibrary *iceLib = PR_LoadLibrary("libICE.so.6");
|
||||
if (!iceLib) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRLibrary *smLib = PR_LoadLibrary("libSM.so.6");
|
||||
if (!smLib) {
|
||||
PR_UnloadLibrary(iceLib);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
IceSetIOErrorHandler = (IceSetIOErrorHandlerFn)PR_FindFunctionSymbol(iceLib, "IceSetIOErrorHandler");
|
||||
IceAddConnectionWatch = (IceAddConnectionWatchFn)PR_FindFunctionSymbol(iceLib, "IceAddConnectionWatch");
|
||||
IceConnectionNumber = (IceConnectionNumberFn)PR_FindFunctionSymbol(iceLib, "IceConnectionNumber");
|
||||
IceProcessMessages = (IceProcessMessagesFn)PR_FindFunctionSymbol(iceLib, "IceProcessMessages");
|
||||
IceGetConnectionContext = (IceGetConnectionContextFn)PR_FindFunctionSymbol(iceLib, "IceGetConnectionContext");
|
||||
if (!IceSetIOErrorHandler || !IceAddConnectionWatch ||
|
||||
!IceConnectionNumber || !IceProcessMessages || !IceGetConnectionContext) {
|
||||
PR_UnloadLibrary(iceLib);
|
||||
PR_UnloadLibrary(smLib);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SmcInteractDone = (SmcInteractDoneFn)PR_FindFunctionSymbol(smLib, "SmcInteractDone");
|
||||
SmcSaveYourselfDone = (SmcSaveYourselfDoneFn)PR_FindFunctionSymbol(smLib, "SmcSaveYourselfDone");
|
||||
SmcInteractRequest = (SmcInteractRequestFn)PR_FindFunctionSymbol(smLib, "SmcInteractRequest");
|
||||
SmcCloseConnection = (SmcCloseConnectionFn)PR_FindFunctionSymbol(smLib, "SmcCloseConnection");
|
||||
SmcOpenConnection = (SmcOpenConnectionFn)PR_FindFunctionSymbol(smLib, "SmcOpenConnection");
|
||||
SmcSetProperties = (SmcSetPropertiesFn)PR_FindFunctionSymbol(smLib, "SmcSetProperties");
|
||||
if (!SmcInteractDone || !SmcSaveYourselfDone || !SmcInteractRequest ||
|
||||
!SmcCloseConnection || !SmcOpenConnection || !SmcSetProperties) {
|
||||
PR_UnloadLibrary(iceLib);
|
||||
PR_UnloadLibrary(smLib);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ice_init();
|
||||
|
||||
// all callbacks are mandatory in libSM 1.0, so listen even if we don't care.
|
||||
unsigned long mask = SmcSaveYourselfProcMask | SmcDieProcMask |
|
||||
SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
|
||||
|
||||
SmcCallbacks callbacks;
|
||||
callbacks.save_yourself.callback = nsNativeAppSupportUnix::SaveYourselfCB;
|
||||
callbacks.save_yourself.client_data = static_cast<SmPointer>(this);
|
||||
|
||||
callbacks.die.callback = nsNativeAppSupportUnix::DieCB;
|
||||
callbacks.die.client_data = static_cast<SmPointer>(this);
|
||||
|
||||
callbacks.save_complete.callback = nsNativeAppSupportUnix::SaveCompleteCB;
|
||||
callbacks.save_complete.client_data = nullptr;
|
||||
|
||||
callbacks.shutdown_cancelled.callback =
|
||||
nsNativeAppSupportUnix::ShutdownCancelledCB;
|
||||
callbacks.shutdown_cancelled.client_data = static_cast<SmPointer>(this);
|
||||
|
||||
char errbuf[256];
|
||||
mSessionConnection = SmcOpenConnection(nullptr, this, SmProtoMajor,
|
||||
SmProtoMinor, mask, &callbacks,
|
||||
prev_client_id.get(), &client_id,
|
||||
sizeof(errbuf), errbuf);
|
||||
}
|
||||
|
||||
if (!mSessionConnection) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
_gnome_program_init_fn gnome_program_init =
|
||||
(_gnome_program_init_fn)PR_FindFunctionSymbol(gnomeLib, "gnome_program_init");
|
||||
_gnome_program_get_fn gnome_program_get =
|
||||
(_gnome_program_get_fn)PR_FindFunctionSymbol(gnomeLib, "gnome_program_get");
|
||||
_libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)PR_FindFunctionSymbol(gnomeuiLib, "libgnomeui_module_info_get");
|
||||
if (!gnome_program_init || !gnome_program_get || !libgnomeui_module_info_get) {
|
||||
PR_UnloadLibrary(gnomeuiLib);
|
||||
PR_UnloadLibrary(gnomeLib);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
// We will load gail, atk-bridge by ourself later
|
||||
// We can't run atk-bridge init here, because gail get the control
|
||||
// Set GNOME_ACCESSIBILITY to 0 can avoid this
|
||||
static const char *accEnv = "GNOME_ACCESSIBILITY";
|
||||
const char *accOldValue = getenv(accEnv);
|
||||
setenv(accEnv, "0", 1);
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
if (!gnome_program_get()) {
|
||||
gnome_program_init("Gecko", "1.0", libgnomeui_module_info_get(),
|
||||
gArgc, gArgv, nullptr);
|
||||
}
|
||||
#endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (accOldValue) {
|
||||
setenv(accEnv, accOldValue, 1);
|
||||
LogModule::Init(); // need to make sure initialized before SetClientState
|
||||
if (prev_client_id.IsEmpty() ||
|
||||
(client_id && !prev_client_id.Equals(client_id))) {
|
||||
SetClientState(STATE_REGISTERING);
|
||||
} else {
|
||||
unsetenv(accEnv);
|
||||
SetClientState(STATE_IDLE);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Careful! These libraries cannot be unloaded after this point because
|
||||
// gnome_program_init causes atexit handlers to be registered. Strange
|
||||
// crashes will occur if these libraries are unloaded.
|
||||
gdk_x11_set_sm_client_id(client_id);
|
||||
|
||||
// TODO GTK3 - see Bug 694570 - Stop using libgnome and libgnomeui on Linux
|
||||
#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
|
||||
gnome_client_set_restart_command = (_gnome_client_set_restart_command_fn)
|
||||
PR_FindFunctionSymbol(gnomeuiLib, "gnome_client_set_restart_command");
|
||||
// Set SM Properties
|
||||
// SmCloneCommand, SmProgram, SmRestartCommand, SmUserID are required
|
||||
// properties so must be set, and must have a sensible fallback value.
|
||||
|
||||
_gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
|
||||
PR_FindFunctionSymbol(gnomeuiLib, "gnome_master_client");
|
||||
|
||||
GnomeClient *client = gnome_master_client();
|
||||
g_signal_connect(client, "save-yourself", G_CALLBACK(save_yourself_cb), nullptr);
|
||||
g_signal_connect(client, "die", G_CALLBACK(die_cb), nullptr);
|
||||
|
||||
// Set the correct/requested restart command in any case.
|
||||
// Determine executable path to use for XSMP session restore
|
||||
|
||||
// Is there a request to suppress default binary launcher?
|
||||
nsAutoCString path;
|
||||
char* argv1 = getenv("MOZ_APP_LAUNCHER");
|
||||
nsAutoCString path(getenv("MOZ_APP_LAUNCHER"));
|
||||
|
||||
if(!argv1) {
|
||||
// Tell the desktop the command for restarting us so that we can be part of XSMP session restore
|
||||
if (path.IsEmpty()) {
|
||||
NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!");
|
||||
nsCOMPtr<nsIFile> executablePath;
|
||||
nsresult rv;
|
||||
@ -258,14 +621,60 @@ nsNativeAppSupportUnix::Start(bool *aRetVal)
|
||||
}
|
||||
|
||||
executablePath->GetNativePath(path);
|
||||
argv1 = (char*)(path.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (argv1) {
|
||||
gnome_client_set_restart_command(client, 1, &argv1);
|
||||
if (path.IsEmpty()) {
|
||||
// can't determine executable path. Best fallback is name from
|
||||
// application.ini but it might not resolve to the same executable at
|
||||
// launch time.
|
||||
path = gAppData->name; // will always be set
|
||||
ToLowerCase(path);
|
||||
MOZ_LOG(sMozSMLog, LogLevel::Warning,
|
||||
("Could not determine executable path. Falling back to %s.", path.get()));
|
||||
}
|
||||
#endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */
|
||||
|
||||
SmProp propRestart, propClone, propProgram, propUser, *props[4];
|
||||
SmPropValue valsRestart[3], valsClone[1], valsProgram[1], valsUser[1];
|
||||
int n = 0;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(kClientIDParam, "--sm-client-id");
|
||||
|
||||
SetSMValue(valsRestart[0], path);
|
||||
SetSMValue(valsRestart[1], kClientIDParam);
|
||||
SetSMValue(valsRestart[2], nsDependentCString(client_id));
|
||||
SetSMProperty(propRestart, SmRestartCommand, SmLISTofARRAY8, 3, valsRestart);
|
||||
props[n++] = &propRestart;
|
||||
|
||||
SetSMValue(valsClone[0], path);
|
||||
SetSMProperty(propClone, SmCloneCommand, SmLISTofARRAY8, 1, valsClone);
|
||||
props[n++] = &propClone;
|
||||
|
||||
nsAutoCString appName(gAppData->name); // will always be set
|
||||
ToLowerCase(appName);
|
||||
|
||||
SetSMValue(valsProgram[0], appName);
|
||||
SetSMProperty(propProgram, SmProgram, SmARRAY8, 1, valsProgram);
|
||||
props[n++] = &propProgram;
|
||||
|
||||
nsAutoCString userName; // username that started the program
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
if (pw && pw->pw_name) {
|
||||
userName = pw->pw_name;
|
||||
} else {
|
||||
userName = NS_LITERAL_CSTRING("nobody");
|
||||
MOZ_LOG(sMozSMLog, LogLevel::Warning,
|
||||
("Could not determine user-name. Falling back to %s.", userName.get()));
|
||||
}
|
||||
|
||||
SetSMValue(valsUser[0], userName);
|
||||
SetSMProperty(propUser, SmUserID, SmARRAY8, 1, valsUser);
|
||||
props[n++] = &propUser;
|
||||
|
||||
SmcSetProperties(mSessionConnection, n, props);
|
||||
|
||||
g_free(client_id);
|
||||
#endif /* MOZ_X11 */
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -5,9 +5,14 @@
|
||||
#ifndef GDKX_WRAPPER_H
|
||||
#define GDKX_WRAPPER_H
|
||||
|
||||
#include <gtk/gtkversion.h>
|
||||
|
||||
#define gdk_x11_window_foreign_new_for_display gdk_x11_window_foreign_new_for_display_
|
||||
#define gdk_x11_window_lookup_for_display gdk_x11_window_lookup_for_display_
|
||||
#define gdk_x11_window_get_xid gdk_x11_window_get_xid_
|
||||
#if !GTK_CHECK_VERSION(2,24,0)
|
||||
#define gdk_x11_set_sm_client_id gdk_x11_set_sm_client_id_
|
||||
#endif
|
||||
#include_next <gdk/gdkx.h>
|
||||
#undef gdk_x11_window_foreign_new_for_display
|
||||
#undef gdk_x11_window_lookup_for_display
|
||||
@ -35,4 +40,12 @@ gdk_x11_window_get_xid(GdkWindow *window)
|
||||
#define GDK_IS_X11_DISPLAY(a) (true)
|
||||
#endif
|
||||
|
||||
#if !GTK_CHECK_VERSION(2,24,0)
|
||||
#undef gdk_x11_set_sm_client_id
|
||||
static inline void
|
||||
gdk_x11_set_sm_client_id (const gchar *sm_client_id)
|
||||
{
|
||||
gdk_set_sm_client_id(sm_client_id);
|
||||
}
|
||||
#endif
|
||||
#endif /* GDKX_WRAPPER_H */
|
||||
|
@ -134,6 +134,7 @@ STUB(gdk_x11_window_foreign_new_for_display)
|
||||
STUB(gdk_x11_window_lookup_for_display)
|
||||
STUB(gdk_x11_window_set_user_time)
|
||||
STUB(gdk_x11_xatom_to_atom)
|
||||
STUB(gdk_x11_set_sm_client_id)
|
||||
STUB(gtk_accel_label_new)
|
||||
STUB(gtk_alignment_get_type)
|
||||
STUB(gtk_alignment_new)
|
||||
|
Loading…
Reference in New Issue
Block a user