Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-06-22 13:46:46 +02:00
commit c2b7d7ae22
27 changed files with 5969 additions and 193 deletions

View File

@ -22,6 +22,7 @@ addons = [
'child_process',
'chrome',
'content-permissions',
'content-script-messages-latency',
'contributors',
'curly-id',
'developers',

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,6 @@
{
"id": "content-script-messages-latency@jetpack",
"main": "./main.js",
"version": "0.0.1"
}

View File

@ -3,6 +3,7 @@
[child_process.xpi]
[chrome.xpi]
[content-permissions.xpi]
[content-script-messages-latency.xpi]
[contributors.xpi]
[curly-id.xpi]
[developers.xpi]

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 () {

View File

@ -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]

View File

@ -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>

View File

@ -34,7 +34,7 @@
color: #fff;
border: 1px solid #111;
border-radius: 2px;
padding: 5px;
padding: 0 5px;
width: 200px;
margin: 0;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();

View File

@ -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) {

View File

@ -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 -->

View File

@ -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>

View File

@ -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;

View File

@ -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":

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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)