diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/calendar/base/modules/calAsyncUtils.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/calendar/base/modules/calAsyncUtils.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/calendar/base/modules/calAsyncUtils.jsm 2015-01-06 13:13:43.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/calendar/base/modules/calAsyncUtils.jsm 2015-01-07 22:14:55.000000000 +0000
@@ -67,7 +67,7 @@
* will be complaints that an argument is missing.
*/
promisifyCalendar: function(aCalendar) {
- return Proxy(aCalendar, promisifyProxyHandler);
+ return new Proxy(aCalendar, promisifyProxyHandler);
},
/**
* Create an operation listener (calIOperationListener) that resolves when
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/calendar/base/src/calUtils.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/calendar/base/src/calUtils.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/calendar/base/src/calUtils.js 2015-01-06 13:13:43.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/calendar/base/src/calUtils.js 2015-01-07 22:14:56.000000000 +0000
@@ -1214,7 +1214,8 @@
try {
iface[func].apply(iface, args ? args : []);
} catch (exc) {
- Components.utils.reportError(exc + "\nSTACK: " + exc.stack);
+ let stack = exc.stack || (exc.location ? exc.location.formattedStack : null);
+ Components.utils.reportError(exc + "\nSTACK: " + stack);
}
}
this.mInterfaces.forEach(notifyFunc);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/debian/changelog thunderbird-trunk-37.0~a1~hg20150107r17323.222415/debian/changelog
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/debian/changelog 2015-01-06 13:41:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/debian/changelog 2015-01-07 22:54:18.000000000 +0000
@@ -1,10 +1,10 @@
-thunderbird-trunk (37.0~a1~hg20150106r17307.222089-0ubuntu1~umd1~trusty) trusty; urgency=medium
+thunderbird-trunk (37.0~a1~hg20150107r17323.222415-0ubuntu1~umd1~trusty) trusty; urgency=medium
* Refresh patches
- update debian/patches/unity-menubar.patch
- update debian/patches/dont-include-hyphenation-patterns.patch
- -- Chris Coulson Mon, 08 Dec 2014 19:22:09 +0000
+ -- Chris Coulson Wed, 07 Jan 2015 22:08:37 +0000
thunderbird-trunk (36.0~a1~hg20141126r17147.217518-0ubuntu1) utopic; urgency=medium
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/changesets thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/changesets
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/changesets 2015-01-06 13:17:57.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/changesets 2015-01-07 22:17:38.000000000 +0000
@@ -7,10 +7,10 @@
ca 671:0427dac6c811
cs 1966:9f298225fdd0
da 735:3da200622aa9
-de 3007:5a874cd81db3
+de 3008:ea13cdb0f61b
el 356:6b23a751385f
en-GB 444:876cf60b4ae5
-es-AR 937:468733829156
+es-AR 938:1562d452f929
es-ES 2325:d08982ade622
et 530:54f9fbfcd622
eu 379:d7df5b30dd48
@@ -24,7 +24,7 @@
hu 794:b45f6b60407e
id 512:b520186368ec
is 297:33af74865c25
-it 3572:42874962c3b0
+it 3576:b2e5691e68ae
ja 633:608df4a0c172
ja-JP-mac 634:aba6f4f0ee5e
ka 56:2ee9bc162267
@@ -39,9 +39,9 @@
pt-PT 2056:22b468633bda
rm 388:945a12b3635b
ro 414:9a349bbcf451
-ru 2259:112b37c18b04
+ru 2260:b1944c9224ca
si 414:fb01300c5c4a
-sk 1232:bce9afe2c04b
+sk 1237:fd7ff14af866
sl 820:a5e5e63844e9
sq 774:4e1d2dc5d752
sr 254:e6fa777ae177
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/de/dom/chrome/appstrings.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/de/dom/chrome/appstrings.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/de/dom/chrome/appstrings.properties 2015-01-06 13:15:50.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/de/dom/chrome/appstrings.properties 2015-01-07 22:16:18.000000000 +0000
@@ -30,6 +30,6 @@
externalProtocolLaunchBtn=Anwendung ausführen
malwareBlocked=Die Seite auf %S wurde als attackierende Seite gemeldet und auf Grund Ihrer Sicherheitseinstellungen blockiert.
phishingBlocked=Die Website auf %S ist als eine gefälschte Seite gemeldet, die erstellt wurde, um Anwender dazu zu verleiten, persönliche oder finanzielle Daten preiszugeben.
-cspBlocked=Diese Seite hat eine Inhaltsichterheitsrichtlinie (Content Security Policy), die verhindert, dass sie auf diese Art geladen wird.
+cspBlocked=Diese Seite hat eine Inhaltsicherheitsrichtlinie (Content Security Policy), die verhindert, dass sie auf diese Art geladen wird.
corruptedContentError=Die Seite, die Sie anzusehen versuchen, kann nicht angezeigt werden, da ein Fehler in der Datenübertragung festgestellt wurde.
remoteXUL=Diese Seite verwendet eine nicht unterstützte Technologie, die nicht mehr standardmäßig unterstützt wird.
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/de/dom/chrome/netError.dtd thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/de/dom/chrome/netError.dtd
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/de/dom/chrome/netError.dtd 2015-01-06 13:15:50.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/de/dom/chrome/netError.dtd 2015-01-07 22:16:18.000000000 +0000
@@ -87,8 +87,8 @@
Diese Arten von Web-Fälschungen werden in Betrugsversuchen verwendet, die als "Phishing-Attacken" bekannt sind. Dabei versuchen Kriminelle in betrügerischer Absicht, Webseiten oder E-Mails nachzuahmen, denen Sie eventuell vertrauen.
">
-
-Der Browser hat das Laden dieser Seite auf diese Art blockiert, weil diese Seite eine Inhaltsichterheitsrichtlinie (Content Security Policy) hat, das nicht erlaubt.
">
+
+Der Browser hat das Laden dieser Seite auf diese Art blockiert, weil diese Seite eine Inhaltsicherheitsrichtlinie (Content Security Policy) hat, das nicht erlaubt.">
Die Seite, die Sie anzusehen versuchen, kann nicht angezeigt werden, da ein Fehler in der Datenübertragung festgestellt wurde.
Bitte kontaktieren Sie die Website-Betreiber, um sie über dieses Problem zu verständigen.
">
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/es-AR/chat/xmpp.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/es-AR/chat/xmpp.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/es-AR/chat/xmpp.properties 2015-01-06 13:16:03.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/es-AR/chat/xmpp.properties 2015-01-07 22:16:24.000000000 +0000
@@ -44,6 +44,9 @@
# fails.
# %S is the name of the MUC.
conversation.error.joinFailed=No se pudo unir: %S
+# These are displayed in a conversation as a system error message.
+conversation.error.remoteServerNotFound=No se pudo alcanzar el servidor del destinatario
+conversation.error.unknownError=Error desconocido
# LOCALIZATION NOTE (tooltip.*):
# These are the titles of lines of information that will appear in
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/ru/chat/xmpp.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/ru/chat/xmpp.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/ru/chat/xmpp.properties 2015-01-06 13:17:04.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/ru/chat/xmpp.properties 2015-01-07 22:17:16.000000000 +0000
@@ -25,6 +25,8 @@
connection.error.failedToGetAResource = Не удалось получить ресурс
conversation.error.notDelivered = Это сообщение не может быть доставлено: %S
conversation.error.joinFailed = Не удалось присоединиться: %S
+conversation.error.remoteServerNotFound = Не удалось связаться с сервером получателя
+conversation.error.unknownError = Неизвестная ошибка
tooltip.status = Статус (%S)
tooltip.statusNoResource = Статус
tooltip.subscription = Подписка
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/ru/mail/chrome/messenger/preferences/advanced.dtd thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/ru/mail/chrome/messenger/preferences/advanced.dtd
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/ru/mail/chrome/messenger/preferences/advanced.dtd 2015-01-06 13:17:04.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/ru/mail/chrome/messenger/preferences/advanced.dtd 2015-01-07 22:17:16.000000000 +0000
@@ -12,6 +12,8 @@
+
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/sk/chat/xmpp.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/sk/chat/xmpp.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/l10n/sk/chat/xmpp.properties 2015-01-06 13:17:07.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/l10n/sk/chat/xmpp.properties 2015-01-07 22:17:20.000000000 +0000
@@ -43,6 +43,9 @@
# fails.
# %S is the name of the MUC.
conversation.error.joinFailed=Nebolo možné sa pripojiť k %S
+# These are displayed in a conversation as a system error message.
+conversation.error.remoteServerNotFound=Nebolo možné spojiť sa so serverom príjemcu
+conversation.error.unknownError=Neznáma chyba
# LOCALIZATION NOTE (tooltip.*):
# These are the titles of lines of information that will appear in
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mail/components/compose/content/addressingWidgetOverlay.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mail/components/compose/content/addressingWidgetOverlay.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mail/components/compose/content/addressingWidgetOverlay.js 2015-01-06 13:13:43.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mail/components/compose/content/addressingWidgetOverlay.js 2015-01-07 22:14:56.000000000 +0000
@@ -119,7 +119,6 @@
var addrReply = "";
var addrNg = "";
var addrFollow = "";
- var addrOther = "";
var to_Sep = "";
var cc_Sep = "";
var bcc_Sep = "";
@@ -168,9 +167,12 @@
case "addr_reply" : addrReply += reply_Sep + recipient; reply_Sep = ","; break;
case "addr_newsgroups" : addrNg += ng_Sep + fieldValue; ng_Sep = ","; break;
case "addr_followup" : addrFollow += follow_Sep + fieldValue; follow_Sep = ","; break;
- // do CRLF, same as PUSH_NEWLINE() in nsMsgSend.h / nsMsgCompUtils.cpp
- // see bug #195965
- case "addr_other" : addrOther += awGetPopupElement(i).selectedItem.getAttribute("label") + " " + fieldValue + "\r\n";break;
+ case "addr_other":
+ let headerName =
+ awGetPopupElement(i).selectedItem.getAttribute("label");
+ headerName = headerName.substring(0, headerName.indexOf(':'));
+ msgCompFields.setHeader(headerName, fieldValue);
+ break;
}
}
i ++;
@@ -182,7 +184,6 @@
msgCompFields.replyTo = addrReply;
msgCompFields.newsgroups = addrNg;
msgCompFields.followupTo = addrFollow;
- msgCompFields.otherRandomHeaders = addrOther;
}
else
dump("Message Compose Error: msgCompFields is null (ExtractRecipients)");
@@ -204,7 +205,6 @@
let msgTo = msgCompFields.to;
let msgCC = msgCompFields.cc;
let msgBCC = msgCompFields.bcc;
- let msgRandomHeaders = msgCompFields.otherRandomHeaders;
let msgNewsgroups = msgCompFields.newsgroups;
let msgFollowupTo = msgCompFields.followupTo;
let havePrimaryRecipient = false;
@@ -226,8 +226,6 @@
if (msgBCC)
awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgBCC, false, {}),
"addr_bcc", newListBoxNode, templateNode);
- if (msgRandomHeaders)
- awSetInputAndPopup(msgRandomHeaders, "addr_other", newListBoxNode, templateNode);
if (msgNewsgroups)
{
awSetInputAndPopup(msgNewsgroups, "addr_newsgroups", newListBoxNode, templateNode);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mail/test/mozmill/composition/test-send-button.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mail/test/mozmill/composition/test-send-button.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mail/test/mozmill/composition/test-send-button.js 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mail/test/mozmill/composition/test-send-button.js 2015-01-07 22:14:56.000000000 +0000
@@ -66,8 +66,8 @@
// On an empty window, Send must be disabled.
check_send_commands_state(cwc, false);
- // On any (even invalid) "To:" addressee input, Send must be enabled.
- setupComposeWin(cwc, "recipient", "", "");
+ // On valid "To:" addressee input, Send must be enabled.
+ setupComposeWin(cwc, "recipient@fake.invalid", "", "");
check_send_commands_state(cwc, true);
// When the addressee is not in To, Cc, Bcc or Newsgroup, disable Send again.
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/public/nsIMsgCompFields.idl thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/public/nsIMsgCompFields.idl
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/public/nsIMsgCompFields.idl 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/public/nsIMsgCompFields.idl 2015-01-07 22:14:56.000000000 +0000
@@ -3,12 +3,16 @@
* 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/. */
-#include "nsISupports.idl"
-#include "nsIMsgAttachment.idl"
-#include "nsISimpleEnumerator.idl"
+#include "msgIStructuredHeaders.idl"
-[scriptable, uuid(4f803e7a-5bd5-4344-9901-7a9ee89be780)]
-interface nsIMsgCompFields : nsISupports {
+interface nsIMsgAttachment;
+interface nsISimpleEnumerator;
+
+/**
+ * A collection of headers and other attributes for building a mail message.
+ */
+[scriptable, uuid(4d251187-8213-467a-90f7-f8f330f59085)]
+interface nsIMsgCompFields : msgIWritableStructuredHeaders {
attribute AString from;
attribute AString replyTo;
@@ -47,8 +51,6 @@
/// Status of manually-activated attachment reminder.
attribute boolean attachmentReminder;
- attribute AString otherRandomHeaders;
-
/**
* Beware that when setting this property, your body must be properly wrapped,
* and the line endings must match MSG_LINEBREAK, namely "\r\n" on Windows
@@ -76,9 +78,6 @@
void ConvertBodyToPlainText();
- // Check if the composing mail headers can be converted to a mail charset.
- boolean checkCharsetConversion(out string fallbackCharset);
-
/**
* Indicates whether we need to check if the current |DocumentCharset|
* can represent all the characters in the message body. It should be
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompFields.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompFields.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompFields.cpp 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompFields.cpp 2015-01-07 22:14:56.000000000 +0000
@@ -9,24 +9,58 @@
#include "nsMsgUtils.h"
#include "prmem.h"
#include "nsIFileChannel.h"
+#include "nsIMsgAttachment.h"
#include "nsIMsgMdnGenerator.h"
#include "nsServiceManagerUtils.h"
#include "nsMsgMimeCID.h"
#include "nsArrayEnumerator.h"
#include "nsMemory.h"
+#include "mozilla/ArrayUtils.h"
#include "mozilla/mailnews/MimeHeaderParser.h"
using namespace mozilla::mailnews;
-/* the following macro actually implement addref, release and query interface for our component. */
-NS_IMPL_ISUPPORTS(nsMsgCompFields, nsIMsgCompFields)
-
+struct HeaderInfo {
+ /// Header name
+ const char *mName;
+ /// If true, nsMsgCompFields should reflect the raw header value instead of
+ /// the unstructured header value.
+ bool mStructured;
+};
+
+// This is a mapping of the m_headers local set to the actual header name we
+// store on the structured header object.
+static HeaderInfo kHeaders[] = {
+ { "From", true },
+ { "Reply-To", true },
+ { "To", true },
+ { "Cc", true },
+ { "Bcc", true },
+ { nullptr, false },
+ { nullptr, false },
+ { "Newsgroups", true },
+ { "Followup-To", true },
+ { "Subject", false },
+ { "Organization", false },
+ { "References", true },
+ { "X-Mozilla-News-Host", false },
+ { "X-Priority", false },
+ { nullptr, false },
+ { "Message-Id", true },
+ { "X-Template", true },
+ { nullptr, false }
+};
+
+static_assert(MOZ_ARRAY_LENGTH(kHeaders) ==
+ nsMsgCompFields::MSG_MAX_HEADERS,
+ "These two arrays need to be kept in sync or bad things will happen!");
+
+NS_IMPL_ISUPPORTS(nsMsgCompFields, nsIMsgCompFields, msgIStructuredHeaders,
+ msgIWritableStructuredHeaders)
+
nsMsgCompFields::nsMsgCompFields()
+: mStructuredHeaders(do_CreateInstance(NS_ISTRUCTUREDHEADERS_CONTRACTID))
{
- int16_t i;
- for (i = 0; i < MSG_MAX_HEADERS; i ++)
- m_headers[i] = nullptr;
-
m_body.Truncate();
m_attachVCard = false;
@@ -51,9 +85,6 @@
nsMsgCompFields::~nsMsgCompFields()
{
- int16_t i;
- for (i = 0; i < MSG_MAX_HEADERS; i ++)
- PR_FREEIF(m_headers[i]);
}
nsresult nsMsgCompFields::SetAsciiHeader(MsgHeaderID header, const char *value)
@@ -61,27 +92,23 @@
NS_ASSERTION(header >= 0 && header < MSG_MAX_HEADERS,
"Invalid message header index!");
- nsresult rv = NS_OK;
- char* old = m_headers[header]; /* Done with careful paranoia, in case the
- value given is the old value (or worse,
- a substring of the old value, as does
- happen here and there.)
- */
- if (value != old)
+ // If we are storing this on the structured header object, we need to set the
+ // value on that object as well. Note that the value may be null, which we'll
+ // take as an attempt to delete the header.
+ const char *headerName = kHeaders[header].mName;
+ if (headerName)
{
- if (value)
- {
- m_headers[header] = strdup(value);
- if (!m_headers[header])
- rv = NS_ERROR_OUT_OF_MEMORY;
- }
- else
- m_headers[header] = nullptr;
+ if (!value || !*value)
+ return mStructuredHeaders->DeleteHeader(headerName);
- PR_FREEIF(old);
+ return mStructuredHeaders->SetRawHeader(headerName,
+ nsDependentCString(value), "UTF-8");
}
- return rv;
+ // Not on the structurd header object, so save it locally.
+ m_headers[header] = value;
+
+ return NS_OK;
}
const char* nsMsgCompFields::GetAsciiHeader(MsgHeaderID header)
@@ -89,7 +116,24 @@
NS_ASSERTION(header >= 0 && header < MSG_MAX_HEADERS,
"Invalid message header index!");
- return m_headers[header] ? m_headers[header] : "";
+ const char *headerName = kHeaders[header].mName;
+ if (headerName)
+ {
+ // We may be out of sync with the structured header object. Retrieve the
+ // header value.
+ if (kHeaders[header].mStructured)
+ {
+ mStructuredHeaders->GetRawHeader(headerName, m_headers[header]);
+ }
+ else
+ {
+ nsString value;
+ mStructuredHeaders->GetUnstructuredHeader(headerName, value);
+ CopyUTF16toUTF8(value, m_headers[header]);
+ }
+ }
+
+ return m_headers[header].get();
}
nsresult nsMsgCompFields::SetUnicodeHeader(MsgHeaderID header, const nsAString& value)
@@ -235,16 +279,6 @@
return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
-NS_IMETHODIMP nsMsgCompFields::SetOtherRandomHeaders(const nsAString &value)
-{
- return SetUnicodeHeader(MSG_OTHERRANDOMHEADERS_HEADER_ID, value);
-}
-
-NS_IMETHODIMP nsMsgCompFields::GetOtherRandomHeaders(nsAString &_retval)
-{
- return GetUnicodeHeader(MSG_OTHERRANDOMHEADERS_HEADER_ID, _retval);
-}
-
NS_IMETHODIMP nsMsgCompFields::SetNewspostUrl(const char *value)
{
return SetAsciiHeader(MSG_NEWSPOSTURL_HEADER_ID, value);
@@ -521,7 +555,7 @@
ExtractEmails(header, results);
else
ExtractDisplayAddresses(header, results);
-
+
uint32_t count = results.Length();
char16_t **result = (char16_t **)NS_Alloc(sizeof(char16_t *) * count);
for (uint32_t i = 0; i < count; ++i)
@@ -592,21 +626,6 @@
return *aDefaultCharacterSet ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
-NS_IMETHODIMP nsMsgCompFields::CheckCharsetConversion(char **fallbackCharset, bool *_retval)
-{
- NS_ENSURE_ARG_POINTER(_retval);
-
- nsAutoCString headers;
- for (int16_t i = 0; i < MSG_MAX_HEADERS; i++)
- headers.Append(m_headers[i]);
-
- // charset conversion check
- *_retval = nsMsgI18Ncheck_data_in_charset_range(GetCharacterSet(), NS_ConvertUTF8toUTF16(headers.get()).get(),
- fallbackCharset);
-
- return NS_OK;
-}
-
NS_IMETHODIMP nsMsgCompFields::GetNeedToCheckCharset(bool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompFields.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompFields.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompFields.h 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompFields.h 2015-01-07 22:14:56.000000000 +0000
@@ -33,8 +33,15 @@
/* this macro defines QueryInterface, AddRef and Release for this class */
NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_MSGISTRUCTUREDHEADERS(mStructuredHeaders->)
+ NS_FORWARD_MSGIWRITABLESTRUCTUREDHEADERS(mStructuredHeaders->)
NS_DECL_NSIMSGCOMPFIELDS
+ // Allow the C++ utility methods for people who use a concrete class instead
+ // of the interfaces.
+ using msgIStructuredHeaders::GetAddressingHeader;
+ using msgIWritableStructuredHeaders::SetAddressingHeader;
+
typedef enum MsgHeaderID
{
MSG_FROM_HEADER_ID = 0,
@@ -49,7 +56,6 @@
MSG_SUBJECT_HEADER_ID,
MSG_ORGANIZATION_HEADER_ID,
MSG_REFERENCES_HEADER_ID,
- MSG_OTHERRANDOMHEADERS_HEADER_ID,
MSG_NEWSPOSTURL_HEADER_ID,
MSG_PRIORITY_HEADER_ID,
MSG_CHARACTER_SET_HEADER_ID,
@@ -107,9 +113,6 @@
const char* GetReferences() {return GetAsciiHeader(MSG_REFERENCES_HEADER_ID);}
- nsresult SetOtherRandomHeaders(const char *value) {return SetAsciiHeader(MSG_OTHERRANDOMHEADERS_HEADER_ID, value);}
- const char* GetOtherRandomHeaders() {return GetAsciiHeader(MSG_OTHERRANDOMHEADERS_HEADER_ID);}
-
const char* GetNewspostUrl() {return GetAsciiHeader(MSG_NEWSPOSTURL_HEADER_ID);}
const char* GetPriority() {return GetAsciiHeader(MSG_PRIORITY_HEADER_ID);}
@@ -140,7 +143,7 @@
protected:
virtual ~nsMsgCompFields();
- char* m_headers[MSG_MAX_HEADERS];
+ nsCString m_headers[MSG_MAX_HEADERS];
nsCString m_body;
nsCOMArray m_attachments;
bool m_attachVCard;
@@ -156,6 +159,7 @@
bool m_needToCheckCharset;
nsCOMPtr mSecureCompFields;
+ nsCOMPtr mStructuredHeaders;
};
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompose.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompose.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompose.cpp 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompose.cpp 2015-01-07 22:14:56.000000000 +0000
@@ -63,6 +63,7 @@
#include "nsUConvCID.h"
#include "nsIUnicodeNormalizer.h"
#include "nsIMsgAccountManager.h"
+#include "nsIMsgAttachment.h"
#include "nsIMsgProgress.h"
#include "nsMsgFolderFlags.h"
#include "nsIMsgDatabase.h"
@@ -5376,34 +5377,12 @@
NS_ENSURE_ARG_POINTER(identity);
NS_ENSURE_ARG_POINTER(_retval);
- nsresult rv = m_compFields->CheckCharsetConversion(fallbackCharset, _retval);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (*_retval)
- {
- nsString fullName;
- nsString organization;
- nsAutoString identityStrings;
-
- rv = identity->GetFullName(fullName);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!fullName.IsEmpty())
- identityStrings.Append(fullName);
-
- rv = identity->GetOrganization(organization);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!organization.IsEmpty())
- identityStrings.Append(organization);
-
- if (!identityStrings.IsEmpty())
- {
- // use fallback charset if that's already set
- const char *charset = (fallbackCharset && *fallbackCharset) ? *fallbackCharset : m_compFields->GetCharacterSet();
- *_retval = nsMsgI18Ncheck_data_in_charset_range(charset, identityStrings.get(),
- fallbackCharset);
- }
- }
-
+ // Kept around for legacy reasons. This method is supposed to check that the
+ // headers can be converted to the appropriate charset, but we don't support
+ // encoding headers to non-UTF-8, so this is now moot.
+ if (fallbackCharset)
+ *fallbackCharset = nullptr;
+ *_retval = true;
return NS_OK;
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompUtils.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompUtils.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompUtils.cpp 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompUtils.cpp 2015-01-07 22:14:56.000000000 +0000
@@ -29,8 +29,10 @@
#include "nsMemory.h"
#include "nsCRTGlue.h"
#include
+#include "mozilla/mailnews/Services.h"
#include "mozilla/Services.h"
#include "nsIMIMEInfo.h"
+#include "nsIMsgHeaderParser.h"
NS_IMPL_ISUPPORTS(nsMsgCompUtils, nsIMsgCompUtils)
@@ -222,170 +224,76 @@
return mime_sanity_check_fields_recipients(to, cc, bcc, newsgroups);
}
-static char *
-nsMsgStripLine (char * string)
-{
- char * ptr;
-
- /* remove leading blanks */
- while(*string=='\t' || *string==' ' || *string=='\r' || *string=='\n')
- string++;
-
- for(ptr=string; *ptr; ptr++)
- ; /* NULL BODY; Find end of string */
-
- /* remove trailing blanks */
- for(ptr--; ptr >= string; ptr--)
- {
- if(*ptr=='\t' || *ptr==' ' || *ptr=='\r' || *ptr=='\n')
- *ptr = '\0';
- else
- break;
- }
-
- return string;
-}
-
//
// Generate the message headers for the new RFC822 message
//
#define UA_PREF_PREFIX "general.useragent."
-#define ENCODE_AND_PUSH(name, structured, body, charset, usemime) \
- { \
- PUSH_STRING((name)); \
- convbuf = nsMsgI18NEncodeMimePartIIStr((body), (structured), (charset), strlen(name), (usemime)); \
- if (convbuf) { \
- PUSH_STRING (convbuf); \
- PR_FREEIF(convbuf); \
- } \
- else \
- PUSH_STRING((body)); \
- PUSH_NEWLINE (); \
- }
-
-char *
-mime_generate_headers (nsMsgCompFields *fields,
- const char *charset,
- nsMsgDeliverMode deliver_mode, nsIPrompt * aPrompt, nsresult *status)
+// Helper macro for generating the X-Mozilla-Draft-Info header.
+#define APPEND_BOOL(method, param) \
+ do { \
+ bool val = false; \
+ fields->Get##method(&val); \
+ if (val) \
+ draftInfo.AppendLiteral(param "=1"); \
+ else \
+ draftInfo.AppendLiteral(param "=0"); \
+ } while (false)
+
+nsresult mime_generate_headers(nsIMsgCompFields *fields,
+ nsMsgDeliverMode deliver_mode,
+ msgIWritableStructuredHeaders *finalHeaders)
{
- nsresult rv;
- *status = NS_OK;
+ nsresult rv = NS_OK;
nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
- if (NS_FAILED(rv)) {
- *status = rv;
- return nullptr;
- }
+ NS_ENSURE_SUCCESS(rv, rv);
- bool usemime = nsMsgMIMEGetConformToStandard();
- int32_t size = 0;
- char *buffer = nullptr, *buffer_tail = nullptr;
bool isDraft =
deliver_mode == nsIMsgSend::nsMsgSaveAsDraft ||
deliver_mode == nsIMsgSend::nsMsgSaveAsTemplate ||
deliver_mode == nsIMsgSend::nsMsgQueueForLater ||
deliver_mode == nsIMsgSend::nsMsgDeliverBackground;
- const char* pFrom;
- const char* pTo;
- const char* pCc;
- const char* pMessageID;
- const char* pReplyTo;
- const char* pOrg;
- const char* pNewsGrp;
- const char* pFollow;
- const char* pSubject;
- const char* pPriority;
- const char* pReference;
- const char* pOtherHdr;
- char *convbuf;
-
bool hasDisclosedRecipient = false;
- nsAutoCString headerBuf; // accumulate header strings to get length
- headerBuf.Truncate();
+ MOZ_ASSERT(fields, "null fields");
+ NS_ENSURE_ARG_POINTER(fields);
- NS_ASSERTION (fields, "null fields");
- if (!fields) {
- *status = NS_ERROR_NULL_POINTER;
- return nullptr;
- }
- pFrom = fields->GetFrom();
- if (pFrom)
- headerBuf.Append(pFrom);
- pReplyTo =fields->GetReplyTo();
- if (pReplyTo)
- headerBuf.Append(pReplyTo);
- pTo = fields->GetTo();
- if (pTo)
- headerBuf.Append(pTo);
- pCc = fields->GetCc();
- if (pCc)
- headerBuf.Append(pCc);
- pNewsGrp = fields->GetNewsgroups(); if (pNewsGrp) size += 3 * PL_strlen (pNewsGrp);
- pFollow= fields->GetFollowupTo(); if (pFollow) size += 3 * PL_strlen (pFollow);
- pSubject = fields->GetSubject();
- if (pSubject)
- headerBuf.Append(pSubject);
- pReference = fields->GetReferences(); if (pReference) size += 3 * PL_strlen (pReference);
- pOrg= fields->GetOrganization();
- if (pOrg)
- headerBuf.Append(pOrg);
- pOtherHdr= fields->GetOtherRandomHeaders(); if (pOtherHdr) size += 3 * PL_strlen (pOtherHdr);
- pPriority = fields->GetPriority(); if (pPriority) size += 3 * PL_strlen (pPriority);
- pMessageID = fields->GetMessageId(); if (pMessageID) size += PL_strlen (pMessageID);
-
- /* Multiply by 3 here to make enough room for MimePartII conversion */
- size += 3 * headerBuf.Length();
-
- /* Add a bunch of slop for the static parts of the headers. */
- /* size += 2048; */
- size += 2560;
-
- buffer = (char *) PR_Malloc (size);
- if (!buffer) {
- *status = NS_ERROR_OUT_OF_MEMORY;
- return nullptr; /* NS_ERROR_OUT_OF_MEMORY */
- }
+ nsCOMArray from;
+ fields->GetAddressingHeader("From", from, true);
- buffer_tail = buffer;
+ // Copy all headers from the original compose field.
+ rv = finalHeaders->AddAllHeaders(fields);
+ NS_ENSURE_SUCCESS(rv, rv);
- if (pMessageID && *pMessageID) {
- PUSH_STRING ("Message-ID: ");
- PUSH_STRING (pMessageID);
- PUSH_NEWLINE ();
+ bool hasMessageId = false;
+ if (NS_SUCCEEDED(fields->HasHeader("Message-ID", &hasMessageId)) &&
+ hasMessageId)
+ {
/* MDN request header requires to have MessageID header presented
* in the message in order to
* coorelate the MDN reports to the original message. Here will be
* the right place
*/
- if (fields->GetReturnReceipt() &&
+ bool returnReceipt = false;
+ fields->GetReturnReceipt(&returnReceipt);
+ if (returnReceipt &&
(deliver_mode != nsIMsgSend::nsMsgSaveAsDraft &&
deliver_mode != nsIMsgSend::nsMsgSaveAsTemplate))
{
- int32_t receipt_header_type = nsIMsgMdnGenerator::eDntType;
- fields->GetReceiptHeaderType(&receipt_header_type);
+ int32_t receipt_header_type = nsIMsgMdnGenerator::eDntType;
+ fields->GetReceiptHeaderType(&receipt_header_type);
// nsIMsgMdnGenerator::eDntType = MDN Disposition-Notification-To: ;
// nsIMsgMdnGenerator::eRrtType = Return-Receipt-To: ;
// nsIMsgMdnGenerator::eDntRrtType = both MDN DNT and RRT headers .
if (receipt_header_type != nsIMsgMdnGenerator::eRrtType)
- ENCODE_AND_PUSH(
- "Disposition-Notification-To: ", true, pFrom, charset, usemime);
+ finalHeaders->SetAddressingHeader("Disposition-Notification-To", from);
if (receipt_header_type != nsIMsgMdnGenerator::eDntType)
- ENCODE_AND_PUSH(
- "Return-Receipt-To: ", true, pFrom, charset, usemime);
- }
-
-#ifdef SUPPORT_X_TEMPLATE_NAME
- if (deliver_mode == MSG_SaveAsTemplate) {
- const char *pStr = fields->GetTemplateName();
- pStr = pStr ? pStr : "";
- ENCODE_AND_PUSH("X-Template: ", false, pStr, charset, usemime);
+ finalHeaders->SetAddressingHeader("Return-Receipt-To", from);
}
-#endif /* SUPPORT_X_TEMPLATE_NAME */
}
PRExplodedTime now;
@@ -397,74 +305,49 @@
PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
per RFC 1123 (superceding RFC 822.)
*/
- PR_FormatTimeUSEnglish(buffer_tail, 100,
- "Date: %a, %d %b %Y %H:%M:%S ",
+ char dateString[130];
+ PR_FormatTimeUSEnglish(dateString, sizeof(dateString),
+ "%a, %d %b %Y %H:%M:%S ",
&now);
- buffer_tail += PL_strlen (buffer_tail);
- PR_snprintf(buffer_tail, buffer + size - buffer_tail,
+ char *entryPoint = dateString + strlen(dateString);
+ PR_snprintf(entryPoint, sizeof(dateString) - (entryPoint - dateString),
"%c%02d%02d" CRLF,
(gmtoffset >= 0 ? '+' : '-'),
((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
- buffer_tail += PL_strlen (buffer_tail);
-
- if (pFrom && *pFrom)
- {
- ENCODE_AND_PUSH("From: ", true, pFrom, charset, usemime);
- }
-
- if (pReplyTo && *pReplyTo)
- {
- ENCODE_AND_PUSH("Reply-To: ", true, pReplyTo, charset, usemime);
- }
-
- if (pOrg && *pOrg)
- {
- ENCODE_AND_PUSH("Organization: ", false, pOrg, charset, usemime);
- }
+ finalHeaders->SetRawHeader("Date", nsDependentCString(dateString), nullptr);
// X-Mozilla-Draft-Info
if (isDraft)
{
- PUSH_STRING(HEADER_X_MOZILLA_DRAFT_INFO);
- PUSH_STRING(": internal/draft; ");
- if (fields->GetAttachVCard())
- PUSH_STRING("vcard=1");
- else
- PUSH_STRING("vcard=0");
- PUSH_STRING("; ");
- if (fields->GetReturnReceipt()) {
+ nsAutoCString draftInfo;
+ draftInfo.AppendLiteral("internal/draft; ");
+ APPEND_BOOL(AttachVCard, "vcard");
+ draftInfo.AppendLiteral("; ");
+ bool hasReturnReceipt = false;
+ fields->GetReturnReceipt(&hasReturnReceipt);
+ if (hasReturnReceipt)
+ {
// slight change compared to 4.x; we used to use receipt= to tell
// whether the draft/template has request for either MDN or DNS or both
// return receipt; since the DNS is out of the picture we now use the
// header type + 1 to tell whether user has requested the return receipt
int32_t headerType = 0;
fields->GetReceiptHeaderType(&headerType);
- char *type = PR_smprintf("%d", (int)headerType + 1);
- if (type)
- {
- PUSH_STRING("receipt=");
- PUSH_STRING(type);
- PR_FREEIF(type);
- }
+ draftInfo.AppendLiteral("receipt=");
+ draftInfo.AppendInt(headerType + 1);
}
else
- PUSH_STRING("receipt=0");
- PUSH_STRING("; ");
- if (fields->GetDSN())
- PUSH_STRING("DSN=1");
- else
- PUSH_STRING("DSN=0");
- PUSH_STRING("; ");
- PUSH_STRING("uuencode=0");
- PUSH_STRING("; ");
- if (fields->GetAttachmentReminder())
- PUSH_STRING("attachmentreminder=1");
- else
- PUSH_STRING("attachmentreminder=0");
+ draftInfo.AppendLiteral("receipt=0");
+ draftInfo.AppendLiteral("; ");
+ APPEND_BOOL(DSN, "DSN");
+ draftInfo.AppendLiteral("; ");
+ draftInfo.AppendLiteral("uuencode=0");
+ draftInfo.AppendLiteral("; ");
+ APPEND_BOOL(AttachmentReminder, "attachmentreminder");
- PUSH_NEWLINE ();
+ finalHeaders->SetRawHeader(HEADER_X_MOZILLA_DRAFT_INFO, draftInfo, nullptr);
}
@@ -475,152 +358,69 @@
pHTTPHandler->GetUserAgent(userAgentString);
if (!userAgentString.IsEmpty())
- {
- PUSH_STRING ("User-Agent: ");
- PUSH_STRING(userAgentString.get());
- PUSH_NEWLINE ();
- }
+ finalHeaders->SetUnstructuredHeader("User-Agent",
+ NS_ConvertUTF8toUTF16(userAgentString));
}
- PUSH_STRING ("MIME-Version: 1.0" CRLF);
+ finalHeaders->SetUnstructuredHeader("MIME-Version", NS_LITERAL_STRING("1.0"));
- if (pNewsGrp && *pNewsGrp) {
- /* turn whitespace into a comma list
- */
- char *duppedNewsGrp = PL_strdup(pNewsGrp);
- if (!duppedNewsGrp) {
- PR_FREEIF(buffer);
- *status = NS_ERROR_OUT_OF_MEMORY;
- return nullptr; /* NS_ERROR_OUT_OF_MEMORY */
- }
- char *n2 = nsMsgStripLine(duppedNewsGrp);
-
- for(char *ptr = n2; *ptr != '\0'; ptr++) {
- /* find first non white space */
- while(!IS_SPACE(*ptr) && *ptr != ',' && *ptr != '\0')
- ptr++;
-
- if(*ptr == '\0')
- break;
-
- if(*ptr != ',')
- *ptr = ',';
-
- /* find next non white space */
- char *ptr2 = ptr+1;
- while(IS_SPACE(*ptr2))
- ptr2++;
-
- if(ptr2 != ptr+1)
- PL_strcpy(ptr+1, ptr2);
- }
-
- // we need to decide the Newsgroup related headers
- // to write to the outgoing message. In ANY case, we need to write the
- // "Newsgroup" header which is the "proper" header as opposed to the
- // HEADER_X_MOZILLA_NEWSHOST which can contain the "news:" URL's.
- //
- // Since n2 can contain data in the form of:
+ nsAutoCString newsgroups;
+ finalHeaders->GetRawHeader("Newsgroups", newsgroups);
+ if (!newsgroups.IsEmpty())
+ {
+ // Since the newsgroup header can contain data in the form of:
// "news://news.mozilla.org/netscape.test,news://news.mozilla.org/netscape.junk"
// we need to turn that into: "netscape.test,netscape.junk"
- //
- nsCOMPtr nntpService = do_GetService("@mozilla.org/messenger/nntpservice;1", &rv);
- if (NS_FAILED(rv) || !nntpService) {
- *status = NS_ERROR_FAILURE;
- return nullptr;
- }
+ // (XXX: can it really?)
+ nsCOMPtr nntpService =
+ do_GetService("@mozilla.org/messenger/nntpservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
nsCString newsgroupsHeaderVal;
nsCString newshostHeaderVal;
- rv = nntpService->GenerateNewsHeaderValsForPosting(nsDependentCString(n2), getter_Copies(newsgroupsHeaderVal), getter_Copies(newshostHeaderVal));
- if (NS_FAILED(rv))
- {
- *status = rv;
- return nullptr;
- }
-
- // fixme:the newsgroups header had better be encoded as the server-side
- // character encoding, but this |charset| might be different from it.
- ENCODE_AND_PUSH("Newsgroups: ", false, newsgroupsHeaderVal.get(),
- charset, false);
+ rv = nntpService->GenerateNewsHeaderValsForPosting(newsgroups,
+ getter_Copies(newsgroupsHeaderVal), getter_Copies(newshostHeaderVal));
+ NS_ENSURE_SUCCESS(rv, rv);
+ finalHeaders->SetRawHeader("Newsgroups", newsgroupsHeaderVal, nullptr);
// If we are here, we are NOT going to send this now. (i.e. it is a Draft,
// Send Later file, etc...). Because of that, we need to store what the user
// typed in on the original composition window for use later when rebuilding
// the headers
- if (deliver_mode != nsIMsgSend::nsMsgDeliverNow && deliver_mode != nsIMsgSend::nsMsgSendUnsent)
+ if (deliver_mode != nsIMsgSend::nsMsgDeliverNow &&
+ deliver_mode != nsIMsgSend::nsMsgSendUnsent)
{
// This is going to be saved for later, that means we should just store
// what the user typed into the "Newsgroup" line in the HEADER_X_MOZILLA_NEWSHOST
// header for later use by "Send Unsent Messages", "Drafts" or "Templates"
- PUSH_STRING (HEADER_X_MOZILLA_NEWSHOST);
- PUSH_STRING (": ");
- PUSH_STRING (newshostHeaderVal.get());
- PUSH_NEWLINE ();
- }
-
- PR_FREEIF(duppedNewsGrp);
- hasDisclosedRecipient = true;
- }
-
- /* #### shamelessly duplicated from above */
- if (pFollow && *pFollow) {
- /* turn whitespace into a comma list
- */
- char *duppedFollowup = PL_strdup(pFollow);
- if (!duppedFollowup) {
- PR_FREEIF(buffer);
- return nullptr; /* NS_ERROR_OUT_OF_MEMORY */
- }
- char *n2 = nsMsgStripLine (duppedFollowup);
-
- for (char *ptr = n2; *ptr != '\0'; ptr++) {
- /* find first non white space */
- while(!IS_SPACE(*ptr) && *ptr != ',' && *ptr != '\0')
- ptr++;
-
- if(*ptr == '\0')
- break;
-
- if(*ptr != ',')
- *ptr = ',';
-
- /* find next non white space */
- char *ptr2 = ptr+1;
- while(IS_SPACE(*ptr2))
- ptr2++;
-
- if(ptr2 != ptr+1)
- PL_strcpy(ptr+1, ptr2);
+ finalHeaders->SetRawHeader(HEADER_X_MOZILLA_NEWSHOST, newshostHeaderVal,
+ nullptr);
}
- ENCODE_AND_PUSH("Followup-To: ", false, n2, charset, false);
- PR_Free (duppedFollowup);
- }
-
- if (pTo && *pTo) {
- ENCODE_AND_PUSH("To: ", true, pTo, charset, usemime);
+ // Newsgroups are a recipient...
hasDisclosedRecipient = true;
}
- if (pCc && *pCc) {
- ENCODE_AND_PUSH("CC: ", true, pCc, charset, usemime);
- hasDisclosedRecipient = true;
- }
+ nsCOMArray recipients;
+ finalHeaders->GetAddressingHeader("To", recipients);
+ hasDisclosedRecipient |= !recipients.IsEmpty();
+ finalHeaders->GetAddressingHeader("Cc", recipients);
+ hasDisclosedRecipient |= !recipients.IsEmpty();
// If we don't have disclosed recipient (only Bcc), address the message to
// undisclosed-recipients to prevent problem with some servers
// If we are saving the message as a draft, don't bother inserting the undisclosed recipients field. We'll take care of that when we
// really send the message.
- if (!hasDisclosedRecipient && !isDraft)
+ if (!hasDisclosedRecipient && !isDraft)
{
bool bAddUndisclosedRecipients = true;
prefs->GetBoolPref("mail.compose.add_undisclosed_recipients", &bAddUndisclosedRecipients);
if (bAddUndisclosedRecipients)
{
- const char* pBcc = fields->GetBcc(); //Do not free me!
- if (pBcc && *pBcc)
+ bool hasBcc = false;
+ fields->HasHeader("Bcc", &hasBcc);
+ if (hasBcc)
{
nsCOMPtr stringService =
mozilla::services::GetStringBundleService();
@@ -635,26 +435,32 @@
getter_Copies(undisclosedRecipients));
if (NS_SUCCEEDED(rv) && !undisclosedRecipients.IsEmpty())
{
- PUSH_STRING("To: ");
- PUSH_STRING(NS_LossyConvertUTF16toASCII(undisclosedRecipients).get());
- PUSH_STRING(":;");
- PUSH_NEWLINE ();
- }
+ nsCOMPtr headerParser(
+ mozilla::services::GetHeaderParser());
+ nsCOMPtr group;
+ headerParser->MakeGroupObject(undisclosedRecipients,
+ nullptr, 0, getter_AddRefs(group));
+ recipients.AppendElement(group);
+ finalHeaders->SetAddressingHeader("To", recipients);
+ }
}
}
}
}
}
- if (pSubject && *pSubject)
- ENCODE_AND_PUSH("Subject: ", false, pSubject, charset, usemime);
+ // We don't want to emit a Bcc header to the output. If we are saving this to
+ // Drafts/Sent, this is readded later in nsMsgSend.cpp.
+ finalHeaders->DeleteHeader("bcc");
// Skip no or empty priority.
- if (pPriority && *pPriority)
+ nsAutoCString priority;
+ rv = fields->GetRawHeader("X-Priority", priority);
+ if (NS_SUCCEEDED(rv) && !priority.IsEmpty())
{
nsMsgPriorityValue priorityValue;
- NS_MsgGetPriorityFromString(pPriority, priorityValue);
+ NS_MsgGetPriorityFromString(priority.get(), priorityValue);
// Skip default priority.
if (priorityValue != nsMsgPriority::Default) {
@@ -665,70 +471,47 @@
NS_MsgGetUntranslatedPriorityName(priorityValue, priorityName);
// Output format: [X-Priority: ()]
- PUSH_STRING("X-Priority: ");
- PUSH_STRING(priorityValueString.get());
- PUSH_STRING(" (");
- PUSH_STRING(priorityName.get());
- PUSH_STRING(")");
- PUSH_NEWLINE();
+ priorityValueString.AppendLiteral(" (");
+ priorityValueString += priorityName;
+ priorityValueString.AppendLiteral(")");
+ finalHeaders->SetRawHeader("X-Priority", priorityValueString, nullptr);
}
}
- if (pReference && *pReference) {
- PUSH_STRING ("References: ");
- if (PL_strlen(pReference) >= 986) {
- char *references = PL_strdup(pReference);
- char *trimAt = PL_strchr(references+1, '<');
- char *ptr;
- // per sfraser, RFC 1036 - a message header line should not exceed
- // 998 characters including the header identifier
- // retiring the earliest reference one after the other
- // but keep the first one for proper threading
- while (references && PL_strlen(references) >= 986 && trimAt) {
- ptr = PL_strchr(trimAt+1, '<');
- if (ptr)
- memmove(trimAt, ptr, PL_strlen(ptr)+1); // including the
- else
- break;
- }
- NS_ASSERTION(references, "null references");
- if (references) {
- PUSH_STRING (references);
- PR_Free(references);
- }
- }
- else
- PUSH_STRING (pReference);
- PUSH_NEWLINE ();
+ nsAutoCString references;
+ finalHeaders->GetRawHeader("References", references);
+ if (!references.IsEmpty())
+ {
+ // The References header should be kept under 998 characters: if it's too
+ // long, trim out the earliest references to make it smaller.
+ if (references.Length() > 986)
{
- const char *lastRef = PL_strrchr(pReference, '<');
-
- if (lastRef) {
- PUSH_STRING ("In-Reply-To: ");
- PUSH_STRING (lastRef);
- PUSH_NEWLINE ();
+ int32_t firstRef = references.FindChar('<');
+ int32_t secondRef = references.FindChar('<', firstRef + 1);
+ if (secondRef > 0)
+ {
+ nsAutoCString newReferences(StringHead(references, secondRef));
+ int32_t bracket = references.FindChar('<',
+ references.Length() + newReferences.Length() - 986);
+ if (bracket > 0)
+ {
+ newReferences.Append(Substring(references, bracket));
+ finalHeaders->SetRawHeader("References", newReferences, nullptr);
+ }
}
}
+ // The In-Reply-To header is the last entry in the references header...
+ int32_t bracket = references.RFind("<");
+ if (bracket >= 0)
+ finalHeaders->SetRawHeader("In-Reply-To", Substring(references, bracket),
+ nullptr);
}
- if (pOtherHdr && *pOtherHdr) {
- /* Assume they already have the right newlines and continuations
- and so on. for these headers, the PUSH_NEWLINE() happens in addressingWidgetOverlay.js */
- PUSH_STRING (pOtherHdr);
- }
-
- if (buffer_tail > buffer + size - 1) {
- PR_FREEIF(buffer);
- return nullptr;
- }
- /* realloc it smaller... */
- char *newBuffer = (char*) PR_REALLOC(buffer, buffer_tail - buffer + 1);
- if (!newBuffer) // The original bigger buffer is still usable to the caller.
- return buffer;
-
- return newBuffer;
+ return NS_OK;
}
+#undef APPEND_BOOL // X-Mozilla-Draft-Info helper macro
+
static void
GenerateGlobalRandomBytes(unsigned char *buf, int32_t len)
{
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompUtils.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompUtils.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgCompUtils.h 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgCompUtils.h 2015-01-07 22:14:56.000000000 +0000
@@ -69,10 +69,9 @@
const char * /*organization*/,
const char * /*other_random_headers*/);
-char *mime_generate_headers (nsMsgCompFields *fields,
- const char *charset,
- nsMsgDeliverMode deliver_mode,
- nsIPrompt * aPrompt, nsresult *status);
+nsresult mime_generate_headers(nsIMsgCompFields *fields,
+ nsMsgDeliverMode deliver_mode,
+ msgIWritableStructuredHeaders *headers);
char *mime_make_separator(const char *prefix);
char *mime_gen_content_id(uint32_t aPartNum, const char *aEmailAddress);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgSend.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgSend.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgSend.cpp 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgSend.cpp 2015-01-07 22:14:56.000000000 +0000
@@ -64,6 +64,7 @@
#include "nsIMsgAccountManager.h"
#include "nsNativeCharsetUtils.h"
#include "nsIAbCard.h"
+#include "nsIMsgAttachment.h"
#include "nsIMsgProgress.h"
#include "nsIMsgMessageService.h"
#include "nsIMsgHdr.h"
@@ -425,14 +426,9 @@
bool shouldDeleteDeliveryState = true;
nsresult status;
uint32_t i;
- char *headers = 0;
PRFileDesc *in_file = 0;
- bool multipart_p = false;
- bool plaintext_is_mainbody_p = false; // only using text converted from HTML?
char *buffer = 0;
- char *buffer_tail = 0;
nsString msg;
- bool tonews;
bool body_is_us_ascii = true;
nsMsgSendPart* toppart = nullptr; // The very top most container of the message
@@ -465,18 +461,6 @@
bool maincontainerISrelatedpart = false;
const char * toppart_type = nullptr;
- // If we have any attachments, we generate multipart.
- multipart_p = (m_attachment_count > 0);
-
- // to news is true if we have a m_field and we have a Newsgroup and it is not empty
- tonews = false;
- if (mCompFields)
- {
- const char* pstrzNewsgroup = mCompFields->GetNewsgroups();
- if (pstrzNewsgroup && *pstrzNewsgroup)
- tonews = true;
- }
-
status = m_status;
if (NS_FAILED(status))
goto FAIL;
@@ -571,8 +555,6 @@
if (! buffer)
goto FAILMEM;
- buffer_tail = buffer;
-
NS_ASSERTION (m_attachment_pending_count == 0, "m_attachment_pending_count != 0");
mComposeBundle->GetStringFromName(MOZ_UTF16("assemblingMessage"),
@@ -743,8 +725,6 @@
/* Override attachment1_encoding here. */
PR_FREEIF(m_attachment1_encoding);
m_attachment1_encoding = ToNewCString(m_plaintext->m_encoding);
-
- plaintext_is_mainbody_p = true; // converted plaintext is mainbody
}
}
@@ -777,38 +757,35 @@
goto FAIL;
}
- /* Write out the message headers.
- */
- headers = mime_generate_headers (mCompFields, mCompFields->GetCharacterSet(),
- m_deliver_mode, promptObject, &status);
- if (NS_FAILED(status))
- goto FAIL;
+ {
+ nsCOMPtr outputHeaders =
+ do_CreateInstance(NS_ISTRUCTUREDHEADERS_CONTRACTID);
+ status = mime_generate_headers(mCompFields, m_deliver_mode, outputHeaders);
+ if (NS_FAILED(status))
+ goto FAIL;
- if (!headers)
- goto FAILMEM;
+ // Convert the blocks of headers into a single string for emission.
+ nsAutoCString headers;
+ outputHeaders->BuildMimeText(headers);
+
+ // If we converted HTML into plaintext, the plaintext part (plainpart)
+ // already has its content-type and content-transfer-encoding ("other")
+ // headers set.
+ //
+ // In the specific case where such a plaintext part is the top-level message
+ // part (iff an HTML message is being sent as text only and no other
+ // attachments exist) we want to preserve the original plainpart headers,
+ // since they contain accurate transfer encoding and Mac type/creator
+ // information.
+ //
+ // So, in the above case we append the main message headers, otherwise we
+ // overwrite whatever headers may have existed.
+ if (plainpart && plainpart == toppart)
+ status = toppart->AppendOtherHeaders(headers.get());
+ else
+ status = toppart->SetOtherHeaders(headers.get());
+ }
- //
- // If we converted HTML into plaintext, the plaintext part (plainpart)
- // already has its content-type and content-transfer-encoding
- // ("other") headers set.
- //
- // In the specific case where such a plaintext part is the
- // top level message part (iff an HTML message is being sent
- // as text only and no other attachments exist) we want to
- // preserve the original plainpart headers, since they
- // contain accurate transfer encoding and Mac type/creator
- // information.
- //
- // So, in the above case we append the main message headers,
- // otherwise we overwrite whatever headers may have existed.
- //
- /* reordering of headers will happen in nsMsgSendPart::Write */
- if ((plainpart) && (plainpart == toppart))
- status = toppart->AppendOtherHeaders(headers);
- else
- status = toppart->SetOtherHeaders(headers);
- PR_Free(headers);
- headers = nullptr;
if (NS_FAILED(status))
goto FAIL;
@@ -876,7 +853,6 @@
buffer = mime_mailto_stream_read_buffer;
if (! buffer)
goto FAILMEM;
- buffer_tail = buffer;
// Gather all of the attachments for this message that are NOT
// part of an enclosed MHTML message!
@@ -1005,7 +981,6 @@
mainbody = nullptr;
maincontainer = nullptr;
- PR_FREEIF(headers);
if (in_file)
{
PR_Close (in_file);
@@ -2628,45 +2603,6 @@
return NS_OK;
}
-nsresult nsMsgComposeAndSend::SetMimeHeader(nsMsgCompFields::MsgHeaderID header, const char *value)
-{
- char * dupHeader = nullptr;
- nsresult ret = NS_ERROR_OUT_OF_MEMORY;
-
- switch (header)
- {
- case nsMsgCompFields::MSG_FROM_HEADER_ID :
- case nsMsgCompFields::MSG_TO_HEADER_ID :
- case nsMsgCompFields::MSG_REPLY_TO_HEADER_ID :
- case nsMsgCompFields::MSG_CC_HEADER_ID :
- case nsMsgCompFields::MSG_BCC_HEADER_ID :
- dupHeader = mime_fix_addr_header(value);
- break;
-
- case nsMsgCompFields::MSG_NEWSGROUPS_HEADER_ID :
- case nsMsgCompFields::MSG_FOLLOWUP_TO_HEADER_ID :
- dupHeader = mime_fix_news_header(value);
- break;
-
- case nsMsgCompFields::MSG_FCC_HEADER_ID :
- case nsMsgCompFields::MSG_ORGANIZATION_HEADER_ID :
- case nsMsgCompFields::MSG_SUBJECT_HEADER_ID :
- case nsMsgCompFields::MSG_REFERENCES_HEADER_ID :
- case nsMsgCompFields::MSG_X_TEMPLATE_HEADER_ID :
- dupHeader = mime_fix_header(value);
- break;
-
- default : NS_ASSERTION(false, "invalid header"); // unhandled header - bad boy.
- }
-
- if (dupHeader)
- {
- ret = mCompFields->SetAsciiHeader(header, dupHeader);
- PR_Free(dupHeader);
- }
- return ret;
-}
-
nsresult
nsMsgComposeAndSend::InitCompositionFields(nsMsgCompFields *fields,
const nsACString &aOriginalMsgURI,
@@ -2690,21 +2626,6 @@
mCompFields->SetCharacterSet(fields->GetCharacterSet());
}
- pStr = fields->GetMessageId();
- if (pStr)
- {
- mCompFields->SetMessageId((char *) pStr);
- /* Don't bother checking for out of memory; if it fails, then we'll just
- let the server generate the message-id, and suffer with the
- possibility of duplicate messages.*/
- }
-
- pStr = fields->GetNewspostUrl();
- if (pStr && *pStr)
- {
- mCompFields->SetNewspostUrl((char *)pStr);
- }
-
// Now, we will look for a URI defined as the default FCC pref. If this is set,
// then SetFcc will use this value. The FCC field is a URI for the server that
// will hold the "Sent" folder...the
@@ -2741,7 +2662,7 @@
if (folder)
{
useDefaultFCC = false;
- SetMimeHeader(nsMsgCompFields::MSG_FCC_HEADER_ID, fieldsFCC);
+ mCompFields->SetFcc(mime_fix_header(fieldsFCC));
}
}
}
@@ -2835,20 +2756,9 @@
}
}
- mCompFields->SetNewspostUrl((char *) fields->GetNewspostUrl());
-
- /* strip whitespace from and duplicate header fields. */
- SetMimeHeader(nsMsgCompFields::MSG_FROM_HEADER_ID, fields->GetFrom());
- SetMimeHeader(nsMsgCompFields::MSG_REPLY_TO_HEADER_ID, fields->GetReplyTo());
- SetMimeHeader(nsMsgCompFields::MSG_TO_HEADER_ID, fields->GetTo());
- SetMimeHeader(nsMsgCompFields::MSG_CC_HEADER_ID, fields->GetCc());
- SetMimeHeader(nsMsgCompFields::MSG_BCC_HEADER_ID, fields->GetBcc());
- SetMimeHeader(nsMsgCompFields::MSG_NEWSGROUPS_HEADER_ID, fields->GetNewsgroups());
- SetMimeHeader(nsMsgCompFields::MSG_FOLLOWUP_TO_HEADER_ID, fields->GetFollowupTo());
- SetMimeHeader(nsMsgCompFields::MSG_ORGANIZATION_HEADER_ID, fields->GetOrganization());
- SetMimeHeader(nsMsgCompFields::MSG_SUBJECT_HEADER_ID, fields->GetSubject());
- SetMimeHeader(nsMsgCompFields::MSG_REFERENCES_HEADER_ID, fields->GetReferences());
- SetMimeHeader(nsMsgCompFields::MSG_X_TEMPLATE_HEADER_ID, fields->GetTemplateName());
+ // Copy the main bodies of headers over.
+ rv = mCompFields->AddAllHeaders(fields);
+ NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr srcAttachments;
fields->GetAttachments(getter_AddRefs(srcAttachments));
@@ -2866,10 +2776,6 @@
}
}
- pStr = fields->GetOtherRandomHeaders();
- if (pStr)
- mCompFields->SetOtherRandomHeaders((char *) pStr);
-
AddDefaultCustomHeaders();
AddMailFollowupToHeader();
@@ -2916,8 +2822,7 @@
mCompFields->GetBcc(), mCompFields->GetFcc(),
mCompFields->GetNewsgroups(), mCompFields->GetFollowupTo(),
mCompFields->GetSubject(), mCompFields->GetReferences(),
- mCompFields->GetOrganization(),
- mCompFields->GetOtherRandomHeaders());
+ mCompFields->GetOrganization(), "");
}
return NS_OK;
}
@@ -2936,9 +2841,6 @@
int32_t start = 0;
int32_t end = 0;
int32_t len = 0;
- // preserve any custom headers that have been added through the UI
- nsAutoCString newHeaderVal(mCompFields->GetOtherRandomHeaders());
-
while (end != -1) {
end = headersList.FindChar(',', start);
if (end == -1) {
@@ -2954,26 +2856,14 @@
nsCString headerVal;
rv = mUserIdentity->GetCharAttribute(headerName.get(), headerVal);
if (NS_SUCCEEDED(rv)) {
- int32_t colonIdx = headerVal.FindChar(':') + 1;
- if (colonIdx != 0) { // check that the header is *most likely* valid.
- char * convHeader =
- nsMsgI18NEncodeMimePartIIStr(headerVal.get() + colonIdx,
- false,
- mCompFields->GetCharacterSet(),
- colonIdx,
- true);
- if (convHeader) {
- newHeaderVal.Append(Substring(headerVal, 0, colonIdx));
- newHeaderVal.Append(convHeader);
- // we must terminate the header with CRLF here
- // as nsMsgCompUtils.cpp just calls PUSH_STRING
- newHeaderVal.Append("\r\n");
- PR_Free(convHeader);
- }
+ int32_t colonIdx = headerVal.FindChar(':');
+ if (colonIdx > 0) { // check that the header is *most likely* valid.
+ nsCString name(Substring(headerVal, 0, colonIdx));
+ mCompFields->SetRawHeader(name.get(),
+ Substring(headerVal, colonIdx + 1), nullptr);
}
}
}
- mCompFields->SetOtherRandomHeaders(newHeaderVal.get());
}
return rv;
}
@@ -2984,13 +2874,13 @@
nsMsgComposeAndSend::AddMailFollowupToHeader() {
nsresult rv;
- // Get OtherRandomHeaders...
- nsDependentCString customHeaders(mCompFields->GetOtherRandomHeaders());
- // ...and look for MFT-Header. Stop here if MFT is already set.
- NS_NAMED_LITERAL_CSTRING(mftHeaderLabel, "Mail-Followup-To: ");
- if (StringBeginsWith(customHeaders, mftHeaderLabel) ||
- customHeaders.Find("\r\nMail-Followup-To: ") != -1)
+ // If there's already a Mail-Followup-To header, don't need to do anything.
+ nsAutoCString mftHeader;
+ mCompFields->GetRawHeader(HEADER_MAIL_FOLLOWUP_TO, mftHeader);
+ if (!mftHeader.IsEmpty())
+ {
return NS_OK;
+ }
// Get list of subscribed mailing lists
nsAutoCString mailing_lists;
@@ -3034,17 +2924,8 @@
return NS_OK;
// Set Mail-Followup-To
- char * mimeHeader = nsMsgI18NEncodeMimePartIIStr(recipients.get(), true,
- mCompFields->GetCharacterSet(), mftHeaderLabel.Length(), true);
- if (!mimeHeader)
- return NS_ERROR_FAILURE;
-
- customHeaders.Append(mftHeaderLabel);
- customHeaders.Append(mimeHeader);
- customHeaders.AppendLiteral("\r\n");
- mCompFields->SetOtherRandomHeaders(customHeaders.get());
- PR_Free(mimeHeader);
- return NS_OK;
+ return mCompFields->SetRawHeader(HEADER_MAIL_FOLLOWUP_TO, recipients,
+ mCompFields->GetCharacterSet());
}
// Add Mail-Reply-To header
@@ -3053,14 +2934,10 @@
nsMsgComposeAndSend::AddMailReplyToHeader() {
nsresult rv;
- // Get OtherRandomHeaders...
- nsDependentCString customHeaders(mCompFields->GetOtherRandomHeaders());
- // ...and look for MRT-Header. Stop here if MRT is already set.
- NS_NAMED_LITERAL_CSTRING(mrtHeaderLabel, "Mail-Reply-To: ");
- nsAutoCString headers_match = nsAutoCString("\r\n");
- headers_match.Append(mrtHeaderLabel);
- if ((StringHead(customHeaders, mrtHeaderLabel.Length()) == mrtHeaderLabel) ||
- (customHeaders.Find(headers_match) != -1))
+ // If there's already a Mail-Reply-To header, don't need to do anything.
+ nsAutoCString mrtHeader;
+ mCompFields->GetRawHeader(HEADER_MAIL_REPLY_TO, mrtHeader);
+ if (!mrtHeader.IsEmpty())
return NS_OK;
// Get list of reply-to mangling mailing lists
@@ -3119,27 +2996,16 @@
mailReplyTo = mCompFields->GetFrom();
else
mailReplyTo = replyTo;
- char * mimeHeader = nsMsgI18NEncodeMimePartIIStr(mailReplyTo.get(), true,
- mCompFields->GetCharacterSet(), mrtHeaderLabel.Length(), true);
- if (!mimeHeader)
- return NS_ERROR_FAILURE;
- customHeaders.Append(mrtHeaderLabel);
- customHeaders.Append(mimeHeader);
- customHeaders.AppendLiteral("\r\n");
- mCompFields->SetOtherRandomHeaders(customHeaders.get());
- PR_Free(mimeHeader);
+ mCompFields->SetRawHeader(HEADER_MAIL_REPLY_TO, mailReplyTo,
+ mCompFields->GetCharacterSet());
return NS_OK;
}
nsresult
nsMsgComposeAndSend::AddXForwardedMessageIdHeader() {
- nsAutoCString otherHeaders;
- otherHeaders.Append(nsDependentCString(mCompFields->GetOtherRandomHeaders()));
- otherHeaders.Append(NS_LITERAL_CSTRING("X-Forwarded-Message-Id: "));
- otherHeaders.Append(nsDependentCString(mCompFields->GetReferences()));
- otherHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
- return mCompFields->SetOtherRandomHeaders(otherHeaders.get());
+ return mCompFields->SetRawHeader("X-Forwarded-Message-Id",
+ nsDependentCString(mCompFields->GetReferences()), nullptr);
}
nsresult
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgSend.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgSend.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/src/nsMsgSend.h 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/src/nsMsgSend.h 2015-01-07 22:14:56.000000000 +0000
@@ -233,7 +233,6 @@
const nsACString &aOriginalMsgURI,
MSG_ComposeType aType);
- nsresult SetMimeHeader(nsMsgCompFields::MsgHeaderID header, const char *value);
NS_IMETHOD GetBodyFromEditor();
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_expandMailingLists.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_expandMailingLists.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_expandMailingLists.js 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_expandMailingLists.js 2015-01-07 22:14:56.000000000 +0000
@@ -54,10 +54,10 @@
MailServices.ab.directories;
// Test expansion of list with no description.
- checkPopulate("simpson ", "Simpson ,Marge ,Bart ,\"lisa@example.com\" ");
+ checkPopulate("simpson ", "Simpson , Marge , Bart , \"lisa@example.com\" ");
// Test expansion fo list with description.
- checkPopulate("marge ", "Simpson ,Marge ");
+ checkPopulate("marge ", "Simpson , Marge ");
// Test we don't mistake an email address for a list, with a few variations.
checkPopulate("Simpson ", "Simpson ");
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_messageHeaders.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_messageHeaders.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_messageHeaders.js 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_messageHeaders.js 2015-01-07 22:14:56.000000000 +0000
@@ -200,7 +200,7 @@
getBasicSmtpServer());
fields.priority = "high";
fields.references = "";
- fields.otherRandomHeaders = "X-Fake-Header: 124\r\n";
+ fields.setHeader("X-Fake-Header", "124");
let before = Date.now();
let msgHdr = yield richCreateMessage(fields, [], identity);
let after = Date.now();
@@ -268,7 +268,7 @@
yield richCreateMessage(fields, [], identity);
checkDraftHeaders({
// The identity should override the compose fields here.
- "Newsgroups": "mozilla.test,mozilla.test.multimedia",
+ "Newsgroups": "mozilla.test, mozilla.test.multimedia",
"Followup-To": "mozilla.test",
"X-Mozilla-News-Host": "localhost",
});
@@ -496,7 +496,7 @@
});
yield sendMessage({"bcc": "Somebody ",
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_nsIMsgCompFields.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_nsIMsgCompFields.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_nsIMsgCompFields.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_nsIMsgCompFields.js 2015-01-07 22:14:56.000000000 +0000
@@ -0,0 +1,61 @@
+/* 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/. */
+
+/// Test that nsIMsgCompFields works properly
+
+let Ci = Components.interfaces;
+
+let nsMsgCompFields = Components.Constructor(
+ "@mozilla.org/messengercompose/composefields;1",
+ Ci.nsIMsgCompFields);
+
+function check_headers(enumerator, container) {
+ let checkValues = new Set([for (header of container) header.toLowerCase()]);
+ while (enumerator.hasMore()) {
+ let value = enumerator.getNext().toLowerCase();
+ do_check_true(checkValues.has(value));
+ checkValues.delete(value);
+ }
+ do_check_eq(checkValues.size, 0);
+}
+
+function run_test() {
+ let fields = new nsMsgCompFields;
+ do_check_true(fields instanceof Ci.nsIMsgCompFields);
+ do_check_true(fields instanceof Ci.msgIStructuredHeaders);
+ do_check_true(fields instanceof Ci.msgIWritableStructuredHeaders);
+ check_headers(fields.headerNames, []);
+ do_check_false(fields.hasRecipients);
+
+ // Try some basic headers
+ fields.setHeader("From", [{name: "", email: "a@test.invalid"}]);
+ let from = fields.getHeader("from");
+ do_check_eq(from.length, 1);
+ do_check_eq(from[0].email, "a@test.invalid");
+ check_headers(fields.headerNames, ["From"]);
+ do_check_false(fields.hasRecipients);
+
+ // Add a To header
+ fields.setHeader("To", [{name: "", email: "b@test.invalid"}]);
+ check_headers(fields.headerNames, ["From", "To"]);
+ do_check_true(fields.hasRecipients);
+
+ // Delete a header...
+ fields.deleteHeader("from");
+ do_check_eq(fields.getHeader("From"), undefined);
+ check_headers(fields.headerNames, ["To"]);
+
+ // Subject should work and not convert to RFC 2047.
+ fields.subject = "\u79c1\u306f\u4ef6\u540d\u5348\u524d";
+ do_check_eq(fields.subject, "\u79c1\u306f\u4ef6\u540d\u5348\u524d");
+ do_check_eq(fields.getHeader("Subject"),
+ "\u79c1\u306f\u4ef6\u540d\u5348\u524d");
+
+ // Check header synchronization.
+ fields.from = "a@test.invalid";
+ do_check_eq(fields.from, "a@test.invalid");
+ do_check_eq(fields.getHeader("From")[0].email, "a@test.invalid");
+ fields.from = null;
+ do_check_eq(fields.getHeader("From"), undefined);
+}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_nsMsgCompose1.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_nsMsgCompose1.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/test_nsMsgCompose1.js 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/test_nsMsgCompose1.js 2015-01-07 22:14:56.000000000 +0000
@@ -38,7 +38,13 @@
msgCompose.initialize(params);
msgCompose.expandMailingLists();
- do_check_eq(fields.to, aCheckTo);
+ let addresses = fields.getHeader("To");
+ let checkEmails = MailServices.headerParser.parseDecodedHeader(aCheckTo);
+ do_check_eq(addresses.length, checkEmails.length);
+ for (let i = 0; i < addresses.length; i++) {
+ do_check_eq(addresses[i].name, checkEmails[i].name);
+ do_check_eq(addresses[i].email, checkEmails[i].email);
+ }
}
function run_test() {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/xpcshell.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/xpcshell.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/compose/test/unit/xpcshell.ini 2015-01-06 13:13:44.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/compose/test/unit/xpcshell.ini 2015-01-07 22:14:56.000000000 +0000
@@ -12,6 +12,7 @@
[test_expandMailingLists.js]
[test_mailtoURL.js]
[test_messageHeaders.js]
+[test_nsIMsgCompFields.js]
[test_nsMsgCompose1.js]
[test_nsMsgCompose2.js]
[test_nsMsgCompose3.js]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/public/msgIStructuredHeaders.idl thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/public/msgIStructuredHeaders.idl
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/public/msgIStructuredHeaders.idl 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/public/msgIStructuredHeaders.idl 2015-01-07 22:14:57.000000000 +0000
@@ -35,7 +35,7 @@
* exception if the header is missing but rather return an appropriate default
* value as indicated in their documentation.
*/
-[scriptable, uuid(6c105b83-fb73-4282-bd00-05eaf0f4da1d)]
+[scriptable, uuid(e109bf4f-788f-47ba-bfa8-1236ede05597)]
interface msgIStructuredHeaders : nsISupports {
/**
* Retrieve the value of the header stored in this set of headers. If the
@@ -97,6 +97,19 @@
*/
readonly attribute nsIUTF8StringEnumerator headerNames;
+ /**
+ * Retrieve the MIME representation of all of the headers.
+ *
+ * The header values are emitted in an ASCII form, unless internationalized
+ * email addresses are involved. The extra CRLF indicating the end of headers
+ * is not included in this representation.
+ *
+ * This accessor is provided mainly for the benefit of C++ consumers of this
+ * interface, since the JSMime headeremitter functionality allows more
+ * fine-grained customization of the results.
+ */
+ AUTF8String buildMimeText();
+
%{C++
/**
* A special variant of getAddressingHeader that is specialized better for C++
@@ -122,7 +135,7 @@
* An interface that enhances msgIStructuredHeaders by allowing the values of
* headers to be modified.
*/
-[scriptable, uuid(de109a13-47e5-4b69-8784-854b66a88da6)]
+[scriptable, uuid(5dcbbef6-2356-45d8-86d7-b3e73f9c9a0c)]
interface msgIWritableStructuredHeaders : msgIStructuredHeaders {
/**
* Store the given value for the given header, overwriting any previous value
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/public/nsIMimeHeaders.idl thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/public/nsIMimeHeaders.idl
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/public/nsIMimeHeaders.idl 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/public/nsIMimeHeaders.idl 2015-01-07 22:14:57.000000000 +0000
@@ -13,7 +13,7 @@
/**
* An interface that can extract individual headers from a body of headers.
*/
-[scriptable, uuid(8a570955-c641-4471-8cc1-1bc1bca65561)]
+[scriptable, uuid(a9222679-b991-4786-8314-f8819c3a2ba3)]
interface nsIMimeHeaders : msgIStructuredHeaders {
/// Feed in the text of headers
void initialize(in ACString allHeaders);
@@ -29,6 +29,13 @@
*/
ACString extractHeader(in string headerName, in boolean getAllOfThem);
- /// The current text of all header data.
+ /**
+ * The current text of all header data.
+ *
+ * Unlike the asMimeText property, this result preserves the original
+ * representation of the header text, including alternative line endings or
+ * custom, non-8-bit text. For instances of this interface, this attribute is
+ * usually preferable to asMimeText.
+ */
readonly attribute ACString allHeaders;
};
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/extraMimeParsers.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/extraMimeParsers.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/extraMimeParsers.jsm 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/extraMimeParsers.jsm 2015-01-07 22:14:57.000000000 +0000
@@ -0,0 +1,29 @@
+/* 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/. */
+
+function parseNewsgroups(headers) {
+ let ng = [];
+ for (let header of headers) {
+ ng = ng.concat(header.split(/\s*,\s*/));
+ }
+ return ng;
+}
+
+function emitNewsgroups(groups) {
+ // Don't encode the newsgroups names in RFC 2047...
+ if (groups.length == 1)
+ this.addText(groups[0], false);
+ else {
+ this.addText(groups[0], false);
+ for (let i = 1; i < groups.length; i++) {
+ this.addText(", ", true);
+ this.addText(groups[i], false);
+ }
+ }
+}
+
+jsmime.headerparser.addStructuredDecoder("Newsgroups", parseNewsgroups);
+jsmime.headerparser.addStructuredDecoder("Followup-To", parseNewsgroups);
+jsmime.headeremitter.addStructuredEncoder("Newsgroups", emitNewsgroups);
+jsmime.headeremitter.addStructuredEncoder("Followup-To", emitNewsgroups);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/mimedrft.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/mimedrft.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/mimedrft.cpp 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/mimedrft.cpp 2015-01-07 22:14:57.000000000 +0000
@@ -33,6 +33,7 @@
#include "nsIIOService.h"
#include "nsNetUtil.h"
#include "comi18n.h"
+#include "nsIMsgAttachment.h"
#include "nsIMsgCompFields.h"
#include "nsMsgCompCID.h"
#include "nsIMsgComposeService.h"
@@ -326,7 +327,6 @@
const char *organization,
const char *subject,
const char *references,
- const char *other_random_headers,
const char *priority,
const char *newspost_url,
char *charset,
@@ -404,11 +404,6 @@
cFields->SetReferences(!val.IsEmpty() ? val.get() : references);
}
- if (other_random_headers) {
- MIME_DecodeMimeHeader(other_random_headers, charset, false, true, val);
- cFields->SetOtherRandomHeaders(NS_ConvertUTF8toUTF16(!val.IsEmpty() ? val.get() : other_random_headers));
- }
-
if (priority) {
MIME_DecodeMimeHeader(priority, charset, false, true, val);
nsMsgPriorityValue priorityValue;
@@ -1242,7 +1237,7 @@
CreateCompositionFields( from, repl, to, cc, bcc, fcc, grps, foll,
- org, subj, refs, 0, priority, news_host,
+ org, subj, refs, priority, news_host,
mdd->mailcharset,
getter_AddRefs(fields));
@@ -1528,7 +1523,7 @@
else
{
CreateCompositionFields( from, repl, to, cc, bcc, fcc, grps, foll,
- org, subj, refs, 0, priority, news_host,
+ org, subj, refs, priority, news_host,
mdd->mailcharset,
getter_AddRefs(fields));
if (fields)
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/mimeJSComponents.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/mimeJSComponents.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/mimeJSComponents.js 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/mimeJSComponents.js 2015-01-07 22:14:57.000000000 +0000
@@ -40,6 +40,25 @@
};
/**
+ * If we get XPConnect-wrapped objects for msgIAddressObjects, we will have
+ * properties defined for 'group' that throws off jsmime. This function converts
+ * the addresses into the form that jsmime expects.
+ */
+function fixXpconnectAddresses(addrs) {
+ return addrs.map((addr) => {
+ // This is ideally !addr.group, but that causes a JS strict warning, if
+ // group is not in addr, since that's enabled in all chrome code now.
+ if (!('group' in addr) || addr.group === undefined || addr.group === null) {
+ return MimeAddressParser.prototype.makeMailboxObject(addr.name,
+ addr.email);
+ } else {
+ return MimeAddressParser.prototype.makeGroupObject(addr.name,
+ fixXpconnectAddresses(addr.group));
+ }
+ });
+}
+
+/**
* This is a base handler for supporting msgIStructuredHeaders, since we have
* two implementations that need the readable aspects of the interface.
*/
@@ -88,7 +107,22 @@
get headerNames() {
return new StringEnumerator(this._headers.keys());
- }
+ },
+
+ buildMimeText: function () {
+ if (this._headers.size == 0) {
+ return "";
+ }
+ let handler = new HeaderHandler();
+ let emitter = jsmime.headeremitter.makeStreamingEmitter(handler, {
+ useASCII: true
+ });
+ for (let [value, header] of this._headers) {
+ emitter.addStructuredHeader(value, header);
+ }
+ emitter.finish();
+ return handler.value;
+ },
};
@@ -156,7 +190,7 @@
},
setAddressingHeader: function (aHeaderName, aAddresses, aCount) {
- this.setHeader(aHeaderName, aAddresses);
+ this.setHeader(aHeaderName, fixXpconnectAddresses(aAddresses));
},
setRawHeader: function (aHeaderName, aValue, aCharset) {
@@ -237,6 +271,7 @@
},
makeMimeHeader: function (addresses, length) {
+ addresses = fixXpconnectAddresses(addresses);
// Don't output any necessary continuations, so make line length as large as
// possible first.
let options = {
@@ -307,7 +342,7 @@
makeGroupObject: function (aName, aMembers) {
let object = Object.create(EmailGroup);
object.name = aName;
- object.members = aMembers;
+ object.group = aMembers;
return object;
},
@@ -422,7 +457,7 @@
if (aStructured) {
// Structured really means "this is an addressing header"
let addresses = MimeParser.parseHeaderField(aHeader,
- MimeParser.HEADER_ADDRESS);
+ MimeParser.HEADER_ADDRESS | MimeParser.HEADER_OPTION_DECODE_2047);
// This happens in one of our tests if there is a "bare" email but no
// @ sign. Without it, the result disappears since our emission code
// assumes that an empty email is not worth emitting.
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/moz.build thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/moz.build
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/moz.build 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/moz.build 2015-01-07 22:14:57.000000000 +0000
@@ -81,6 +81,7 @@
]
EXTRA_JS_MODULES += [
+ 'extraMimeParsers.jsm',
'jsmime.jsm',
'mimeParser.jsm'
]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/msgMime.manifest thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/msgMime.manifest
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/src/msgMime.manifest 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/src/msgMime.manifest 2015-01-07 22:14:57.000000000 +0000
@@ -6,3 +6,4 @@
contract @mozilla.org/messenger/mimeconverter;1 {93f8c049-80ed-4dda-9000-94ad8daba44c}
contract @mozilla.org/messenger/headerparser;1 {96bd8769-2d0e-4440-963d-22b97fb3ba77}
contract @mozilla.org/messenger/structuredheaders;1 {c560806a-425f-4f0f-bf69-397c58c599a7}
+category custom-mime-encoder A-extra resource:///modules/extraMimeParsers.jsm
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/test/unit/test_structured_headers.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/test/unit/test_structured_headers.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mailnews/mime/test/unit/test_structured_headers.js 2015-01-06 13:13:45.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mailnews/mime/test/unit/test_structured_headers.js 2015-01-07 22:14:57.000000000 +0000
@@ -162,6 +162,30 @@
}
});
+add_task(function* checkBuildMimeText() {
+ let headers = new StructuredHeaders();
+ headers.setHeader("To", [{name: "François Smith", email: "user@☃.invalid"}]);
+ headers.setHeader("From", [{name: "John Doe", email: "jdoe@test.invalid"}]);
+ headers.setHeader("Subject", "A subject that spans a distance quite in " +
+ "excess of 80 characters so as to force an intermediary CRLF");
+ let mimeText =
+ "To: =?UTF-8?Q?Fran=c3=a7ois_Smith?= \r\n" +
+ "From: John Doe \r\n" +
+ "Subject: A subject that spans a distance quite in excess of 80 characters so\r\n" +
+ " as to force an intermediary CRLF\r\n";
+ do_check_eq(headers.buildMimeText(), mimeText);
+
+ // Check the version used for the nsIMimeHeaders implementation. This requires
+ // initializing with a UTF-8 version.
+ let utf8Text = mimeText.replace("☃", "\xe2\x98\x83");
+ let mimeHeaders = Cc["@mozilla.org/messenger/mimeheaders;1"]
+ .createInstance(Ci.nsIMimeHeaders);
+ mimeHeaders.initialize(utf8Text);
+ do_check_eq(mimeHeaders.getHeader("To")[0].email, "user@☃.invalid");
+ do_check_eq(mimeHeaders.buildMimeText(), mimeText);
+ do_check_eq(mimeHeaders.allHeaders, utf8Text);
+});
+
function run_test() {
run_next_test();
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/accessible/base/nsAccessibilityService.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/accessible/base/nsAccessibilityService.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/accessible/base/nsAccessibilityService.h 2015-01-06 13:14:16.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/accessible/base/nsAccessibilityService.h 2015-01-07 22:15:18.000000000 +0000
@@ -251,6 +251,8 @@
inline bool
IPCAccessibilityActive()
{
+ // XXX temporarily disable ipc accessibility because of crashes.
+return false;
#ifdef MOZ_B2G
return false;
#else
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/app/b2g.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/app/b2g.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/app/b2g.js 2015-01-06 13:14:17.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/app/b2g.js 2015-01-07 22:15:18.000000000 +0000
@@ -161,16 +161,8 @@
pref("browser.ssl_override_behavior", 2);
pref("browser.xul.error_pages.expert_bad_cert", false);
-// disable logging for the search service by default
-pref("browser.search.log", false);
-
// disable updating
pref("browser.search.update", false);
-pref("browser.search.update.log", false);
-pref("browser.search.updateinterval", 6);
-
-// enable search suggestions by default
-pref("browser.search.suggest.enabled", true);
// tell the search service that we don't really expose the "current engine"
pref("browser.search.noCurrentEngine", true);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/dolphin/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/dolphin/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/dolphin/sources.xml 2015-01-06 13:14:17.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/dolphin/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -19,13 +19,13 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator-ics/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator-ics/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator-ics/sources.xml 2015-01-06 13:14:17.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator-ics/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -19,13 +19,13 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator-jb/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator-jb/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator-jb/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator-jb/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -17,10 +17,10 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator-kk/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator-kk/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/emulator-kk/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/emulator-kk/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/flame/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/flame/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/flame/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/flame/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -17,10 +17,10 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/flame-kk/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/flame-kk/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/flame-kk/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/flame-kk/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/gaia.json thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/gaia.json
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/gaia.json 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/gaia.json 2015-01-07 22:15:18.000000000 +0000
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "c175a29477e68e70142687587844423a75dcf4cc",
+ "revision": "6505a7b6b4c727a9b561c1220b4ecf726248c5c5",
"repo_path": "integration/gaia-central"
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/hamachi/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/hamachi/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/hamachi/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/hamachi/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -17,11 +17,11 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/helix/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/helix/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/helix/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/helix/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -15,7 +15,7 @@
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/nexus-4/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/nexus-4/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/nexus-4/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/nexus-4/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -17,10 +17,10 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/wasabi/sources.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/wasabi/sources.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/b2g/config/wasabi/sources.xml 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/b2g/config/wasabi/sources.xml 2015-01-07 22:15:18.000000000 +0000
@@ -17,12 +17,12 @@
-
+
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/app/profile/firefox.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/app/profile/firefox.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/app/profile/firefox.js 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/app/profile/firefox.js 2015-01-07 22:15:18.000000000 +0000
@@ -396,9 +396,6 @@
// pointer to the default engine name
pref("browser.search.defaultenginename", "chrome://browser-region/locale/region.properties");
-// disable logging for the search service by default
-pref("browser.search.log", false);
-
// Ordering of Search Engines in the Engine list.
pref("browser.search.order.1", "chrome://browser-region/locale/region.properties");
pref("browser.search.order.2", "chrome://browser-region/locale/region.properties");
@@ -417,34 +414,11 @@
// context menu searches open in the foreground
pref("browser.search.context.loadInBackground", false);
-// send ping to the server to update
-pref("browser.search.update", true);
-
-// disable logging for the search service update system by default
-pref("browser.search.update.log", false);
-
-// Check whether we need to perform engine updates every 6 hours
-pref("browser.search.update.interval", 21600);
-
-// enable search suggestions by default
-pref("browser.search.suggest.enabled", true);
-
pref("browser.search.showOneOffButtons", true);
-#ifdef MOZ_OFFICIAL_BRANDING
-// {moz:official} expands to "official"
-pref("browser.search.official", true);
-#endif
-
// How many times to show the new search highlight
pref("browser.search.highlightCount", 5);
-// geoip end-point and timeout
-pref("browser.search.geoip.url", "https://location.services.mozilla.com/v1/country?key=%MOZILLA_API_KEY%");
-// NOTE: this timeout figure is also the "high" value for the telemetry probe
-// SEARCH_SERVICE_COUNTRY_FETCH_MS - if you change this also change that probe.
-pref("browser.search.geoip.timeout", 2000);
-
pref("browser.sessionhistory.max_entries", 50);
// handle links targeting new windows
@@ -1803,7 +1777,7 @@
#endif
#ifdef NIGHTLY_BUILD
-pref("browser.tabs.remote.autostart.1", true);
+pref("browser.tabs.remote.autostart.1", false);
#endif
// Temporary pref to allow printing in e10s windows on some platforms.
@@ -1816,6 +1790,7 @@
#ifdef NIGHTLY_BUILD
// Enable e10s add-on interposition by default.
pref("extensions.interposition.enabled", true);
+pref("extensions.interposition.prefetching", true);
#endif
pref("browser.defaultbrowser.notificationbar", false);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/browser.css thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/browser.css
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/browser.css 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/browser.css 2015-01-07 22:15:18.000000000 +0000
@@ -418,16 +418,16 @@
/* For results that are actions, their description text is shown instead of
the URL - this needs to follow the locale's direction, unlike URLs. */
-panel:not([noactions]) > richlistbox > richlistitem[type~="action"]:-moz-locale-dir(rtl) > .ac-url-box {
+panel:not([noactions]) > richlistbox > richlistitem.overridable-action:-moz-locale-dir(rtl) > .ac-url-box {
direction: rtl;
}
-panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-url > .ac-action-text,
-panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-action-icon {
+panel[noactions] > richlistbox > richlistitem.overridable-action > .ac-url-box > .ac-url > .ac-action-text,
+panel[noactions] > richlistbox > richlistitem.overridable-action > .ac-url-box > .ac-action-icon {
visibility: collapse;
}
-panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-url > .ac-url-text {
+panel[noactions] > richlistbox > richlistitem.overridable-action > .ac-url-box > .ac-url > .ac-url-text {
visibility: visible;
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/browser-fxaccounts.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/browser-fxaccounts.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/browser-fxaccounts.js 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/browser-fxaccounts.js 2015-01-07 22:15:18.000000000 +0000
@@ -274,11 +274,22 @@
let note = null;
switch (this._migrationInfo.state) {
case this.fxaMigrator.STATE_USER_FXA: {
- let msg = this.strings.GetStringFromName("needUserLong");
- let upgradeLabel =
- this.strings.GetStringFromName("upgradeToFxA.label");
- let upgradeAccessKey =
- this.strings.GetStringFromName("upgradeToFxA.accessKey");
+ // There are 2 cases here - no email address means it is an offer on
+ // the first device (so the user is prompted to create an account).
+ // If there is an email address it is the "join the party" flow, so the
+ // user is prompted to sign in with the address they previously used.
+ let msg, upgradeLabel, upgradeAccessKey;
+ if (this._migrationInfo.email) {
+ msg = this.strings.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
+ [this._migrationInfo.email],
+ 1);
+ upgradeLabel = this.strings.GetStringFromName("signInAfterUpgradeOnOtherDevice.label");
+ upgradeAccessKey = this.strings.GetStringFromName("signInAfterUpgradeOnOtherDevice.accessKey");
+ } else {
+ msg = this.strings.GetStringFromName("needUserLong");
+ upgradeLabel = this.strings.GetStringFromName("upgradeToFxA.label");
+ upgradeAccessKey = this.strings.GetStringFromName("upgradeToFxA.accessKey");
+ }
note = new Weave.Notification(
undefined, msg, undefined, Weave.Notifications.PRIORITY_WARNING, [
new Weave.NotificationButton(upgradeLabel, upgradeAccessKey, () => {
@@ -321,11 +332,9 @@
this.openSignInAgainPage("menupanel");
break;
case "migrate-signup":
- this.fxaMigrator.createFxAccount(window);
- break;
case "migrate-verify":
- // Instead of using the migrator module directly here the UX calls for
- // us to open prefs which has a "resend" button.
+ // The migration flow calls for the menu item to open sync prefs rather
+ // than requesting migration start immediately.
this.openPreferences();
break;
default:
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/browser.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/browser.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/browser.js 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/browser.js 2015-01-07 22:15:18.000000000 +0000
@@ -2082,7 +2082,7 @@
let engine = Services.search.getEngineByAlias(keyword);
if (engine) {
- let submission = engine.getSubmission(param);
+ let submission = engine.getSubmission(param, null, "keyword");
postData = submission.postData;
aCallback({ postData: submission.postData, url: submission.uri.spec,
mayInheritPrincipal: mayInheritPrincipal });
@@ -3568,6 +3568,11 @@
let count = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
count.add(countId);
},
+
+ recordOneoffSearchInTelemetry: function (engine, source, type, where) {
+ let id = this._getSearchEngineId(engine) + "." + source;
+ BrowserUITelemetry.countOneoffSearchEvent(id, type, where);
+ }
};
const SearchHighlight = {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/content-UITour.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/content-UITour.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/content-UITour.js 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/content-UITour.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,91 +0,0 @@
-let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-const PREF_TEST_WHITELIST = "browser.uitour.testingOrigins";
-const UITOUR_PERMISSION = "uitour";
-
-let UITourListener = {
- handleEvent: function (event) {
- if (!Services.prefs.getBoolPref("browser.uitour.enabled")) {
- return;
- }
- if (!this.ensureTrustedOrigin()) {
- return;
- }
- addMessageListener("UITour:SendPageCallback", this);
- addMessageListener("UITour:SendPageNotification", this);
- sendAsyncMessage("UITour:onPageEvent", {detail: event.detail, type: event.type});
- },
-
- isTestingOrigin: function(aURI) {
- if (Services.prefs.getPrefType(PREF_TEST_WHITELIST) != Services.prefs.PREF_STRING) {
- return false;
- }
-
- // Add any testing origins (comma-seperated) to the whitelist for the session.
- for (let origin of Services.prefs.getCharPref(PREF_TEST_WHITELIST).split(",")) {
- try {
- let testingURI = Services.io.newURI(origin, null, null);
- if (aURI.prePath == testingURI.prePath) {
- return true;
- }
- } catch (ex) {
- Cu.reportError(ex);
- }
- }
- return false;
- },
-
- // This function is copied from UITour.jsm.
- isSafeScheme: function(aURI) {
- let allowedSchemes = new Set(["https", "about"]);
- if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
- allowedSchemes.add("http");
-
- if (!allowedSchemes.has(aURI.scheme))
- return false;
-
- return true;
- },
-
- ensureTrustedOrigin: function() {
- if (content.top != content)
- return false;
-
- let uri = content.document.documentURIObject;
-
- if (uri.schemeIs("chrome"))
- return true;
-
- if (!this.isSafeScheme(uri))
- return false;
-
- let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
- if (permission == Services.perms.ALLOW_ACTION)
- return true;
-
- return this.isTestingOrigin(uri);
- },
-
- receiveMessage: function(aMessage) {
- switch (aMessage.name) {
- case "UITour:SendPageCallback":
- this.sendPageEvent("Response", aMessage.data);
- break;
- case "UITour:SendPageNotification":
- this.sendPageEvent("Notification", aMessage.data);
- break;
- }
- },
-
- sendPageEvent: function (type, detail) {
- let doc = content.document;
- let eventName = "mozUITour" + type;
- let event = new doc.defaultView.CustomEvent(eventName, {
- bubbles: true,
- detail: Cu.cloneInto(detail, doc.defaultView)
- });
- doc.dispatchEvent(event);
- }
-};
-
-addEventListener("mozUITour", UITourListener, false, true);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/general/browser_aboutHome.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/general/browser_aboutHome.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/general/browser_aboutHome.js 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/general/browser_aboutHome.js 2015-01-07 22:15:18.000000000 +0000
@@ -439,7 +439,7 @@
info("Waiting for popup to open");
EventUtils.synthesizeMouseAtCenter(searchIcon, {}, gBrowser.selectedBrowser.contentWindow);
yield promiseWaitForEvent(panel, "popupshown");
- ok("Saw popup open");
+ info("Saw popup open");
let promise = promisePrefsOpen();
let item = window.document.getElementById("abouthome-search-panel-manage");
@@ -632,12 +632,33 @@
}
let promisePrefsOpen = Task.async(function*() {
- info("Waiting for the preferences tab to open...");
- let event = yield promiseWaitForEvent(gBrowser.tabContainer, "TabOpen", true);
- let tab = event.target;
- yield promiseTabLoadEvent(tab);
- is(tab.linkedBrowser.currentURI.spec, "about:preferences#search", "Should have seen the prefs tab");
- gBrowser.removeTab(tab);
+ if (Services.prefs.getBoolPref("browser.preferences.inContent")) {
+ info("Waiting for the preferences tab to open...");
+ let event = yield promiseWaitForEvent(gBrowser.tabContainer, "TabOpen", true);
+ let tab = event.target;
+ yield promiseTabLoadEvent(tab);
+ is(tab.linkedBrowser.currentURI.spec, "about:preferences#search", "Should have seen the prefs tab");
+ gBrowser.removeTab(tab);
+ } else {
+ info("Waiting for the preferences window to open...");
+ yield new Promise(resolve => {
+ let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ winWatcher.registerNotification(function onWin(subj, topic, data) {
+ if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+ subj.addEventListener("load", function onLoad() {
+ subj.removeEventListener("load", onLoad);
+ is(subj.document.documentURI, "chrome://browser/content/preferences/preferences.xul", "Should have seen the prefs window");
+ winWatcher.unregisterNotification(onWin);
+ executeSoon(() => {
+ subj.close();
+ resolve();
+ });
+ });
+ }
+ });
+ });
+ }
});
function promiseNewEngine(basename) {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/general/browser_action_keyword_override.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/general/browser_action_keyword_override.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/general/browser_action_keyword_override.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/general/browser_action_keyword_override.js 2015-01-07 22:15:18.000000000 +0000
@@ -0,0 +1,35 @@
+add_task(function*() {
+ // This test is only relevant if UnifiedComplete is enabled.
+ if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
+ todo(false, "Stop supporting old autocomplete components.");
+ return;
+ }
+
+ let itemId =
+ PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+ NetUtil.newURI("http://example.com/?q=%s"),
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ "test");
+ PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
+
+ registerCleanupFunction(() => {
+ PlacesUtils.bookmarks.removeItem(itemId);
+ });
+
+ yield promiseAutocompleteResultPopup("keyword search");
+ let result = gURLBar.popup.richlistbox.children[0];
+
+ info("Before override");
+ is_element_hidden(result._url, "URL element should be hidden");
+ is_element_visible(result._extra, "Extra element should be visible");
+
+ info("During override");
+ EventUtils.synthesizeKey("VK_SHIFT" , { type: "keydown" });
+ is_element_hidden(result._url, "URL element should be hidden");
+ is_element_visible(result._extra, "Extra element should be visible");
+
+ EventUtils.synthesizeKey("VK_SHIFT" , { type: "keyup" });
+
+ gURLBar.popup.hidePopup();
+ yield promisePopupHidden(gURLBar.popup);
+});
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/general/browser.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/general/browser.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/general/browser.ini 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/general/browser.ini 2015-01-07 22:15:18.000000000 +0000
@@ -112,6 +112,7 @@
skip-if = e10s # Bug 1093153 - no about:home support yet
[browser_aboutSyncProgress.js]
[browser_action_keyword.js]
+[browser_action_keyword_override.js]
[browser_action_searchengine.js]
[browser_action_searchengine_alias.js]
[browser_addKeywordSearch.js]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/newtab/browser.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/newtab/browser.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/test/newtab/browser.ini 2015-01-06 13:14:18.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/test/newtab/browser.ini 2015-01-07 22:15:19.000000000 +0000
@@ -45,4 +45,3 @@
[browser_newtab_undo.js]
[browser_newtab_unpin.js]
[browser_newtab_update.js]
-skip-if = true # Bug 1008029
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/urlbarBindings.xml thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/urlbarBindings.xml
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/content/urlbarBindings.xml 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/content/urlbarBindings.xml 2015-01-07 22:15:19.000000000 +0000
@@ -923,6 +923,12 @@
var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
// Check for unmodified left-click, and use default behavior
+ var searchBar = BrowserSearch.searchBar;
+ searchBar.telemetrySearchDetails = {
+ index: controller.selection.currentIndex,
+ kind: "mouse"
+ };
+
if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
!aEvent.altKey && !aEvent.metaKey) {
controller.handleEnter(true);
@@ -930,7 +936,6 @@
}
// Check for middle-click or modified clicks on the search bar
- var searchBar = BrowserSearch.searchBar;
if (searchBar && searchBar.textbox == this.mInput) {
// Handle search bar popup clicks
var search = controller.getValueAt(this.selectedIndex);
@@ -996,7 +1001,7 @@
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/jar.mn thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/jar.mn
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/base/jar.mn 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/base/jar.mn 2015-01-07 22:15:19.000000000 +0000
@@ -77,7 +77,6 @@
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
* content/browser/chatWindow.xul (content/chatWindow.xul)
content/browser/content.js (content/content.js)
- content/browser/content-UITour.js (content/content-UITour.js)
content/browser/defaultthemes/1.footer.jpg (content/defaultthemes/1.footer.jpg)
content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg)
content/browser/defaultthemes/1.icon.jpg (content/defaultthemes/1.icon.jpg)
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/conversation.html thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/conversation.html
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/conversation.html 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/conversation.html 2015-01-07 22:15:19.000000000 +0000
@@ -20,7 +20,7 @@
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/contacts.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/contacts.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/contacts.js 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/contacts.js 2015-01-07 22:15:19.000000000 +0000
@@ -81,7 +81,7 @@
contact[field][0].value = value;
};
- const ContactDropdown = React.createClass({displayName: 'ContactDropdown',
+ const ContactDropdown = React.createClass({displayName: "ContactDropdown",
propTypes: {
handleAction: React.PropTypes.func.isRequired,
canEdit: React.PropTypes.bool
@@ -124,36 +124,36 @@
: "block_contact_menu_button";
return (
- React.DOM.ul({className: cx({ "dropdown-menu": true,
+ React.createElement("ul", {className: cx({ "dropdown-menu": true,
"dropdown-menu-up": this.state.openDirUp })},
- React.DOM.li({className: cx({ "dropdown-menu-item": true,
+ React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": this.props.blocked }),
onClick: this.onItemClick,
- 'data-action': "video-call"},
- React.DOM.i({className: "icon icon-video-call"}),
+ "data-action": "video-call"},
+ React.createElement("i", {className: "icon icon-video-call"}),
mozL10n.get("video_call_menu_button")
),
- React.DOM.li({className: cx({ "dropdown-menu-item": true,
+ React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": this.props.blocked }),
- onClick: this.onItemClick, 'data-action': "audio-call"},
- React.DOM.i({className: "icon icon-audio-call"}),
+ onClick: this.onItemClick, "data-action": "audio-call"},
+ React.createElement("i", {className: "icon icon-audio-call"}),
mozL10n.get("audio_call_menu_button")
),
- React.DOM.li({className: cx({ "dropdown-menu-item": true,
+ React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": !this.props.canEdit }),
- onClick: this.onItemClick, 'data-action': "edit"},
- React.DOM.i({className: "icon icon-edit"}),
+ onClick: this.onItemClick, "data-action": "edit"},
+ React.createElement("i", {className: "icon icon-edit"}),
mozL10n.get("edit_contact_menu_button")
),
- React.DOM.li({className: "dropdown-menu-item",
- onClick: this.onItemClick, 'data-action': blockAction},
- React.DOM.i({className: "icon icon-" + blockAction}),
+ React.createElement("li", {className: "dropdown-menu-item",
+ onClick: this.onItemClick, "data-action": blockAction},
+ React.createElement("i", {className: "icon icon-" + blockAction}),
mozL10n.get(blockLabel)
),
- React.DOM.li({className: cx({ "dropdown-menu-item": true,
+ React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": !this.props.canEdit }),
- onClick: this.onItemClick, 'data-action': "remove"},
- React.DOM.i({className: "icon icon-remove"}),
+ onClick: this.onItemClick, "data-action": "remove"},
+ React.createElement("i", {className: "icon icon-remove"}),
mozL10n.get("remove_contact_menu_button")
)
)
@@ -161,7 +161,7 @@
}
});
- const ContactDetail = React.createClass({displayName: 'ContactDetail',
+ const ContactDetail = React.createClass({displayName: "ContactDetail",
getInitialState: function() {
return {
showMenu: false,
@@ -231,23 +231,23 @@
});
return (
- React.DOM.li({className: contactCSSClass, onMouseLeave: this.hideDropdownMenu},
- React.DOM.div({className: "avatar"}),
- React.DOM.div({className: "details"},
- React.DOM.div({className: "username"}, React.DOM.strong(null, names.firstName), " ", names.lastName,
- React.DOM.i({className: cx({"icon icon-google": this.props.contact.category[0] == "google"})}),
- React.DOM.i({className: cx({"icon icon-blocked": this.props.contact.blocked})})
+ React.createElement("li", {className: contactCSSClass, onMouseLeave: this.hideDropdownMenu},
+ React.createElement("div", {className: "avatar"}),
+ React.createElement("div", {className: "details"},
+ React.createElement("div", {className: "username"}, React.createElement("strong", null, names.firstName), " ", names.lastName,
+ React.createElement("i", {className: cx({"icon icon-google": this.props.contact.category[0] == "google"})}),
+ React.createElement("i", {className: cx({"icon icon-blocked": this.props.contact.blocked})})
),
- React.DOM.div({className: "email"}, email.value)
+ React.createElement("div", {className: "email"}, email.value)
),
- React.DOM.div({className: "icons"},
- React.DOM.i({className: "icon icon-video",
+ React.createElement("div", {className: "icons"},
+ React.createElement("i", {className: "icon icon-video",
onClick: this.handleAction.bind(null, "video-call")}),
- React.DOM.i({className: "icon icon-caret-down",
+ React.createElement("i", {className: "icon icon-caret-down",
onClick: this.showDropdownMenu})
),
this.state.showMenu
- ? ContactDropdown({handleAction: this.handleAction,
+ ? React.createElement(ContactDropdown, {handleAction: this.handleAction,
canEdit: this.canEdit(),
blocked: this.props.contact.blocked})
: null
@@ -257,7 +257,7 @@
}
});
- const ContactsList = React.createClass({displayName: 'ContactsList',
+ const ContactsList = React.createClass({displayName: "ContactsList",
mixins: [
React.addons.LinkedStateMixin,
loop.shared.mixins.WindowCloseMixin
@@ -467,7 +467,7 @@
let cx = React.addons.classSet;
let viewForItem = item => {
- return ContactDetail({key: item._guid, contact: item,
+ return React.createElement(ContactDetail, {key: item._guid, contact: item,
handleContactAction: this.handleContactAction})
};
@@ -494,33 +494,33 @@
}
return (
- React.DOM.div(null,
- React.DOM.div({className: "content-area"},
- ButtonGroup(null,
- Button({caption: this.state.importBusy
+ React.createElement("div", null,
+ React.createElement("div", {className: "content-area"},
+ React.createElement(ButtonGroup, null,
+ React.createElement(Button, {caption: this.state.importBusy
? mozL10n.get("importing_contacts_progress_button")
: mozL10n.get("import_contacts_button"),
disabled: this.state.importBusy,
onClick: this.handleImportButtonClick},
- React.DOM.div({className: cx({"contact-import-spinner": true,
+ React.createElement("div", {className: cx({"contact-import-spinner": true,
spinner: true,
busy: this.state.importBusy})})
),
- Button({caption: mozL10n.get("new_contact_button"),
+ React.createElement(Button, {caption: mozL10n.get("new_contact_button"),
onClick: this.handleAddContactButtonClick})
),
showFilter ?
- React.DOM.input({className: "contact-filter",
+ React.createElement("input", {className: "contact-filter",
placeholder: mozL10n.get("contacts_search_placesholder"),
valueLink: this.linkState("filter")})
: null
),
- React.DOM.ul({className: "contact-list"},
+ React.createElement("ul", {className: "contact-list"},
shownContacts.available ?
shownContacts.available.sort(this.sortContacts).map(viewForItem) :
null,
shownContacts.blocked && shownContacts.blocked.length > 0 ?
- React.DOM.div({className: "contact-separator"}, mozL10n.get("contacts_blocked_contacts")) :
+ React.createElement("div", {className: "contact-separator"}, mozL10n.get("contacts_blocked_contacts")) :
null,
shownContacts.blocked ?
shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
@@ -531,7 +531,7 @@
}
});
- const ContactDetailsForm = React.createClass({displayName: 'ContactDetailsForm',
+ const ContactDetailsForm = React.createClass({displayName: "ContactDetailsForm",
mixins: [React.addons.LinkedStateMixin],
propTypes: {
@@ -628,27 +628,27 @@
let phoneOrEmailRequired = !this.state.email && !this.state.tel;
return (
- React.DOM.div({className: "content-area contact-form"},
- React.DOM.header(null, this.props.mode == "add"
+ React.createElement("div", {className: "content-area contact-form"},
+ React.createElement("header", null, this.props.mode == "add"
? mozL10n.get("add_contact_button")
: mozL10n.get("edit_contact_title")),
- React.DOM.label(null, mozL10n.get("edit_contact_name_label")),
- React.DOM.input({ref: "name", required: true, pattern: "\\s*\\S.*", type: "text",
+ React.createElement("label", null, mozL10n.get("edit_contact_name_label")),
+ React.createElement("input", {ref: "name", required: true, pattern: "\\s*\\S.*", type: "text",
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("name")}),
- React.DOM.label(null, mozL10n.get("edit_contact_email_label")),
- React.DOM.input({ref: "email", type: "email", required: phoneOrEmailRequired,
+ React.createElement("label", null, mozL10n.get("edit_contact_email_label")),
+ React.createElement("input", {ref: "email", type: "email", required: phoneOrEmailRequired,
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("email")}),
- React.DOM.label(null, mozL10n.get("new_contact_fxos_phone_placeholder")),
- React.DOM.input({ref: "tel", type: "tel", required: phoneOrEmailRequired,
+ React.createElement("label", null, mozL10n.get("new_contact_fxos_phone_placeholder")),
+ React.createElement("input", {ref: "tel", type: "tel", required: phoneOrEmailRequired,
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("tel")}),
- ButtonGroup(null,
- Button({additionalClass: "button-cancel",
+ React.createElement(ButtonGroup, null,
+ React.createElement(Button, {additionalClass: "button-cancel",
caption: mozL10n.get("cancel_button"),
onClick: this.handleCancelButtonClick}),
- Button({additionalClass: "button-accept",
+ React.createElement(Button, {additionalClass: "button-accept",
caption: this.props.mode == "add"
? mozL10n.get("add_contact_button")
: mozL10n.get("edit_contact_done_button"),
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/conversation.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/conversation.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/conversation.js 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/conversation.js 2015-01-07 22:15:19.000000000 +0000
@@ -26,7 +26,7 @@
* Master controller view for handling if incoming or outgoing calls are
* in progress, and hence, which view to display.
*/
- var AppControllerView = React.createClass({displayName: 'AppControllerView',
+ var AppControllerView = React.createClass({displayName: "AppControllerView",
mixins: [Backbone.Events, sharedMixins.WindowCloseMixin],
propTypes: {
@@ -64,7 +64,7 @@
render: function() {
switch(this.state.windowType) {
case "incoming": {
- return (IncomingConversationView({
+ return (React.createElement(IncomingConversationView, {
client: this.props.client,
conversation: this.props.conversation,
sdk: this.props.sdk,
@@ -73,21 +73,21 @@
));
}
case "outgoing": {
- return (OutgoingConversationView({
+ return (React.createElement(OutgoingConversationView, {
store: this.props.conversationStore,
dispatcher: this.props.dispatcher,
feedbackStore: this.props.feedbackStore}
));
}
case "room": {
- return (DesktopRoomConversationView({
+ return (React.createElement(DesktopRoomConversationView, {
dispatcher: this.props.dispatcher,
roomStore: this.props.roomStore,
feedbackStore: this.props.feedbackStore}
));
}
case "failed": {
- return GenericFailureView({cancelCall: this.closeWindow});
+ return React.createElement(GenericFailureView, {cancelCall: this.closeWindow});
}
default: {
// If we don't have a windowType, we don't know what we are yet,
@@ -183,7 +183,7 @@
dispatcher.dispatch(new sharedActions.WindowUnload());
});
- React.renderComponent(AppControllerView({
+ React.render(React.createElement(AppControllerView, {
conversationAppStore: conversationAppStore,
roomStore: roomStore,
feedbackStore: feedbackStore,
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/conversation.jsx thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/conversation.jsx
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/conversation.jsx 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/conversation.jsx 2015-01-07 22:15:19.000000000 +0000
@@ -183,7 +183,7 @@
dispatcher.dispatch(new sharedActions.WindowUnload());
});
- React.renderComponent(@" or "+"
var EMAIL_OR_PHONE_RE = /^(:?\S+@\S+|\+\d+)$/;
- var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
+ var IncomingCallView = React.createClass({displayName: "IncomingCallView",
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
propTypes: {
@@ -213,29 +213,29 @@
});
return (
- React.DOM.div({className: "call-window"},
- CallIdentifierView({video: this.props.video,
+ React.createElement("div", {className: "call-window"},
+ React.createElement(CallIdentifierView, {video: this.props.video,
peerIdentifier: this.props.model.getCallIdentifier(),
urlCreationDate: this.props.model.get("urlCreationDate"),
showIcons: true}),
- React.DOM.div({className: "btn-group call-action-group"},
+ React.createElement("div", {className: "btn-group call-action-group"},
- React.DOM.div({className: "fx-embedded-call-button-spacer"}),
+ React.createElement("div", {className: "fx-embedded-call-button-spacer"}),
- React.DOM.div({className: "btn-chevron-menu-group"},
- React.DOM.div({className: "btn-group-chevron"},
- React.DOM.div({className: "btn-group"},
+ React.createElement("div", {className: "btn-chevron-menu-group"},
+ React.createElement("div", {className: "btn-group-chevron"},
+ React.createElement("div", {className: "btn-group"},
- React.DOM.button({className: "btn btn-decline",
+ React.createElement("button", {className: "btn btn-decline",
onClick: this._handleDecline},
mozL10n.get("incoming_call_cancel_button")
),
- React.DOM.div({className: "btn-chevron", onClick: this.toggleDropdownMenu})
+ React.createElement("div", {className: "btn-chevron", onClick: this.toggleDropdownMenu})
),
- React.DOM.ul({className: dropdownMenuClassesDecline},
- React.DOM.li({className: "btn-block", onClick: this._handleDeclineBlock},
+ React.createElement("ul", {className: dropdownMenuClassesDecline},
+ React.createElement("li", {className: "btn-block", onClick: this._handleDeclineBlock},
mozL10n.get("incoming_call_cancel_and_block_button")
)
)
@@ -243,11 +243,11 @@
)
),
- React.DOM.div({className: "fx-embedded-call-button-spacer"}),
+ React.createElement("div", {className: "fx-embedded-call-button-spacer"}),
- AcceptCallButton({mode: this._answerModeProps()}),
+ React.createElement(AcceptCallButton, {mode: this._answerModeProps()}),
- React.DOM.div({className: "fx-embedded-call-button-spacer"})
+ React.createElement("div", {className: "fx-embedded-call-button-spacer"})
)
)
@@ -260,7 +260,7 @@
* Incoming call view accept button, renders different primary actions
* (answer with video / with audio only) based on the props received
**/
- var AcceptCallButton = React.createClass({displayName: 'AcceptCallButton',
+ var AcceptCallButton = React.createClass({displayName: "AcceptCallButton",
propTypes: {
mode: React.PropTypes.object.isRequired,
@@ -270,17 +270,17 @@
var mode = this.props.mode;
return (
/* jshint ignore:start */
- React.DOM.div({className: "btn-chevron-menu-group"},
- React.DOM.div({className: "btn-group"},
- React.DOM.button({className: "btn btn-accept",
+ React.createElement("div", {className: "btn-chevron-menu-group"},
+ React.createElement("div", {className: "btn-group"},
+ React.createElement("button", {className: "btn btn-accept",
onClick: mode.primary.handler,
title: mozL10n.get(mode.primary.tooltip)},
- React.DOM.span({className: "fx-embedded-answer-btn-text"},
+ React.createElement("span", {className: "fx-embedded-answer-btn-text"},
mozL10n.get("incoming_call_accept_button")
),
- React.DOM.span({className: mode.primary.className})
+ React.createElement("span", {className: mode.primary.className})
),
- React.DOM.div({className: mode.secondary.className,
+ React.createElement("div", {className: mode.secondary.className,
onClick: mode.secondary.handler,
title: mozL10n.get(mode.secondary.tooltip)}
)
@@ -297,7 +297,7 @@
* XXX Based on CallFailedView, but built specially until we flux-ify the
* incoming call views (bug 1088672).
*/
- var GenericFailureView = React.createClass({displayName: 'GenericFailureView',
+ var GenericFailureView = React.createClass({displayName: "GenericFailureView",
mixins: [sharedMixins.AudioMixin],
propTypes: {
@@ -312,11 +312,11 @@
document.title = mozL10n.get("generic_failure_title");
return (
- React.DOM.div({className: "call-window"},
- React.DOM.h2(null, mozL10n.get("generic_failure_title")),
+ React.createElement("div", {className: "call-window"},
+ React.createElement("h2", null, mozL10n.get("generic_failure_title")),
- React.DOM.div({className: "btn-group call-action-group"},
- React.DOM.button({className: "btn btn-cancel",
+ React.createElement("div", {className: "btn-group call-action-group"},
+ React.createElement("button", {className: "btn btn-cancel",
onClick: this.props.cancelCall},
mozL10n.get("cancel_button")
)
@@ -332,7 +332,7 @@
*
* At the moment, it does more than that, these parts need refactoring out.
*/
- var IncomingConversationView = React.createClass({displayName: 'IncomingConversationView',
+ var IncomingConversationView = React.createClass({displayName: "IncomingConversationView",
mixins: [sharedMixins.AudioMixin, sharedMixins.WindowCloseMixin],
propTypes: {
@@ -385,7 +385,7 @@
document.title = mozL10n.get("incoming_call_title2");
return (
- IncomingCallView({
+ React.createElement(IncomingCallView, {
model: this.props.conversation,
video: this.props.conversation.hasVideoStream("incoming")}
)
@@ -397,7 +397,7 @@
var callType = this.props.conversation.get("selectedCallType");
return (
- sharedViews.ConversationView({
+ React.createElement(sharedViews.ConversationView, {
initiate: true,
sdk: this.props.sdk,
model: this.props.conversation,
@@ -408,7 +408,7 @@
case "end": {
// XXX To be handled with the "failed" view state when bug 1047410 lands
if (this.state.callFailed) {
- return GenericFailureView({
+ return React.createElement(GenericFailureView, {
cancelCall: this.closeWindow.bind(this)}
);
}
@@ -418,7 +418,7 @@
this.play("terminated");
return (
- sharedViews.FeedbackView({
+ React.createElement(sharedViews.FeedbackView, {
feedbackStore: this.props.feedbackStore,
onAfterFeedbackReceived: this.closeWindow.bind(this)}
)
@@ -426,7 +426,7 @@
}
case "close": {
this.closeWindow();
- return (React.DOM.div(null));
+ return (React.createElement("div", null));
}
}
},
@@ -646,7 +646,7 @@
* View for pending conversations. Displays a cancel button and appropriate
* pending/ringing strings.
*/
- var PendingConversationView = React.createClass({displayName: 'PendingConversationView',
+ var PendingConversationView = React.createClass({displayName: "PendingConversationView",
mixins: [sharedMixins.AudioMixin],
propTypes: {
@@ -686,12 +686,12 @@
});
return (
- ConversationDetailView({contact: this.props.contact},
+ React.createElement(ConversationDetailView, {contact: this.props.contact},
- React.DOM.p({className: "btn-label"}, pendingStateString),
+ React.createElement("p", {className: "btn-label"}, pendingStateString),
- React.DOM.div({className: "btn-group call-action-group"},
- React.DOM.button({className: btnCancelStyles,
+ React.createElement("div", {className: "btn-group call-action-group"},
+ React.createElement("button", {className: btnCancelStyles,
onClick: this.cancelCall},
mozL10n.get("initiate_call_cancel_button")
)
@@ -705,7 +705,7 @@
/**
* Call failed view. Displayed when a call fails.
*/
- var CallFailedView = React.createClass({displayName: 'CallFailedView',
+ var CallFailedView = React.createClass({displayName: "CallFailedView",
mixins: [
Backbone.Events,
sharedMixins.AudioMixin,
@@ -758,7 +758,7 @@
if (!this.state.emailLinkError) {
return;
}
- return React.DOM.p({className: "error"}, mozL10n.get("unable_retrieve_url"));
+ return React.createElement("p", {className: "error"}, mozL10n.get("unable_retrieve_url"));
},
retryCall: function() {
@@ -783,23 +783,23 @@
render: function() {
return (
- React.DOM.div({className: "call-window"},
- React.DOM.h2(null, mozL10n.get("generic_failure_title")),
+ React.createElement("div", {className: "call-window"},
+ React.createElement("h2", null, mozL10n.get("generic_failure_title")),
- React.DOM.p({className: "btn-label"}, mozL10n.get("generic_failure_with_reason2")),
+ React.createElement("p", {className: "btn-label"}, mozL10n.get("generic_failure_with_reason2")),
this._renderError(),
- React.DOM.div({className: "btn-group call-action-group"},
- React.DOM.button({className: "btn btn-cancel",
+ React.createElement("div", {className: "btn-group call-action-group"},
+ React.createElement("button", {className: "btn btn-cancel",
onClick: this.cancelCall},
mozL10n.get("cancel_button")
),
- React.DOM.button({className: "btn btn-info btn-retry",
+ React.createElement("button", {className: "btn btn-info btn-retry",
onClick: this.retryCall},
mozL10n.get("retry_call_button")
),
- React.DOM.button({className: "btn btn-info btn-email",
+ React.createElement("button", {className: "btn btn-info btn-email",
onClick: this.emailLink,
disabled: this.state.emailLinkButtonDisabled},
mozL10n.get("share_button2")
@@ -810,7 +810,7 @@
}
});
- var OngoingConversationView = React.createClass({displayName: 'OngoingConversationView',
+ var OngoingConversationView = React.createClass({displayName: "OngoingConversationView",
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
video: React.PropTypes.object,
@@ -924,15 +924,15 @@
});
return (
- React.DOM.div({className: "video-layout-wrapper"},
- React.DOM.div({className: "conversation"},
- React.DOM.div({className: "media nested"},
- React.DOM.div({className: "video_wrapper remote_wrapper"},
- React.DOM.div({className: "video_inner remote"})
+ React.createElement("div", {className: "video-layout-wrapper"},
+ React.createElement("div", {className: "conversation"},
+ React.createElement("div", {className: "media nested"},
+ React.createElement("div", {className: "video_wrapper remote_wrapper"},
+ React.createElement("div", {className: "video_inner remote"})
),
- React.DOM.div({className: localStreamClasses})
+ React.createElement("div", {className: localStreamClasses})
),
- loop.shared.views.ConversationToolbar({
+ React.createElement(loop.shared.views.ConversationToolbar, {
video: this.props.video,
audio: this.props.audio,
publishStream: this.publishStream,
@@ -947,7 +947,7 @@
* Master View Controller for outgoing calls. This manages
* the different views that need displaying.
*/
- var OutgoingConversationView = React.createClass({displayName: 'OutgoingConversationView',
+ var OutgoingConversationView = React.createClass({displayName: "OutgoingConversationView",
mixins: [
sharedMixins.AudioMixin,
Backbone.Events
@@ -995,7 +995,7 @@
document.title = mozL10n.get("conversation_has_ended");
return (
- sharedViews.FeedbackView({
+ React.createElement(sharedViews.FeedbackView, {
feedbackStore: this.props.feedbackStore,
onAfterFeedbackReceived: this._closeWindow.bind(this)}
)
@@ -1009,14 +1009,14 @@
return null;
}
case CALL_STATES.TERMINATED: {
- return (CallFailedView({
+ return (React.createElement(CallFailedView, {
dispatcher: this.props.dispatcher,
store: this.props.store,
contact: this.state.contact}
));
}
case CALL_STATES.ONGOING: {
- return (OngoingConversationView({
+ return (React.createElement(OngoingConversationView, {
dispatcher: this.props.dispatcher,
video: {enabled: !this.state.videoMuted},
audio: {enabled: !this.state.audioMuted}}
@@ -1032,7 +1032,7 @@
return null;
}
default: {
- return (PendingConversationView({
+ return (React.createElement(PendingConversationView, {
dispatcher: this.props.dispatcher,
callState: this.state.callState,
contact: this.state.contact,
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/panel.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/panel.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/panel.js 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/panel.js 2015-01-07 22:15:19.000000000 +0000
@@ -21,7 +21,7 @@
var ContactsList = loop.contacts.ContactsList;
var ContactDetailsForm = loop.contacts.ContactDetailsForm;
- var TabView = React.createClass({displayName: 'TabView',
+ var TabView = React.createClass({displayName: "TabView",
propTypes: {
buttonsHidden: React.PropTypes.array,
// The selectedTab prop is used by the UI showcase.
@@ -66,28 +66,28 @@
var isSelected = (this.state.selectedTab == tabName);
if (!tab.props.hidden) {
tabButtons.push(
- React.DOM.li({className: cx({selected: isSelected}),
+ React.createElement("li", {className: cx({selected: isSelected}),
key: i,
- 'data-tab-name': tabName,
+ "data-tab-name": tabName,
onClick: this.handleSelectTab})
);
}
tabs.push(
- React.DOM.div({key: i, className: cx({tab: true, selected: isSelected})},
+ React.createElement("div", {key: i, className: cx({tab: true, selected: isSelected})},
tab.props.children
)
);
}, this);
return (
- React.DOM.div({className: "tab-view-container"},
- React.DOM.ul({className: "tab-view"}, tabButtons),
+ React.createElement("div", {className: "tab-view-container"},
+ React.createElement("ul", {className: "tab-view"}, tabButtons),
tabs
)
);
}
});
- var Tab = React.createClass({displayName: 'Tab',
+ var Tab = React.createClass({displayName: "Tab",
render: function() {
return null;
}
@@ -96,7 +96,7 @@
/**
* Availability drop down menu subview.
*/
- var AvailabilityDropdown = React.createClass({displayName: 'AvailabilityDropdown',
+ var AvailabilityDropdown = React.createClass({displayName: "AvailabilityDropdown",
mixins: [sharedMixins.DropdownMenuMixin],
getInitialState: function() {
@@ -142,22 +142,22 @@
mozL10n.get("display_name_available_status");
return (
- React.DOM.div({className: "dropdown"},
- React.DOM.p({className: "dnd-status", onClick: this.showDropdownMenu},
- React.DOM.span(null, availabilityText),
- React.DOM.i({className: availabilityStatus})
+ React.createElement("div", {className: "dropdown"},
+ React.createElement("p", {className: "dnd-status", onClick: this.showDropdownMenu},
+ React.createElement("span", null, availabilityText),
+ React.createElement("i", {className: availabilityStatus})
),
- React.DOM.ul({className: availabilityDropdown,
+ React.createElement("ul", {className: availabilityDropdown,
onMouseLeave: this.hideDropdownMenu},
- React.DOM.li({onClick: this.changeAvailability("available"),
+ React.createElement("li", {onClick: this.changeAvailability("available"),
className: "dropdown-menu-item dnd-make-available"},
- React.DOM.i({className: "status status-available"}),
- React.DOM.span(null, mozL10n.get("display_name_available_status"))
+ React.createElement("i", {className: "status status-available"}),
+ React.createElement("span", null, mozL10n.get("display_name_available_status"))
),
- React.DOM.li({onClick: this.changeAvailability("do-not-disturb"),
+ React.createElement("li", {onClick: this.changeAvailability("do-not-disturb"),
className: "dropdown-menu-item dnd-make-unavailable"},
- React.DOM.i({className: "status status-dnd"}),
- React.DOM.span(null, mozL10n.get("display_name_dnd_status"))
+ React.createElement("i", {className: "status status-dnd"}),
+ React.createElement("span", null, mozL10n.get("display_name_dnd_status"))
)
)
)
@@ -165,7 +165,7 @@
}
});
- var GettingStartedView = React.createClass({displayName: 'GettingStartedView',
+ var GettingStartedView = React.createClass({displayName: "GettingStartedView",
mixins: [sharedMixins.WindowCloseMixin],
handleButtonClick: function() {
@@ -181,13 +181,13 @@
return null;
}
return (
- React.DOM.div({id: "fte-getstarted"},
- React.DOM.header({id: "fte-title"},
+ React.createElement("div", {id: "fte-getstarted"},
+ React.createElement("header", {id: "fte-title"},
mozL10n.get("first_time_experience_title", {
"clientShortname": mozL10n.get("clientShortname2")
})
),
- Button({htmlId: "fte-button",
+ React.createElement(Button, {htmlId: "fte-button",
onClick: this.handleButtonClick,
caption: mozL10n.get("first_time_experience_button_label")})
)
@@ -195,7 +195,7 @@
}
});
- var ToSView = React.createClass({displayName: 'ToSView',
+ var ToSView = React.createClass({displayName: "ToSView",
getInitialState: function() {
var getPref = navigator.mozLoop.getLoopPref.bind(navigator.mozLoop);
@@ -212,28 +212,28 @@
var privacy_notice_url = navigator.mozLoop.getLoopPref('legal.privacy_url');
var tosHTML = mozL10n.get("legal_text_and_links3", {
"clientShortname": mozL10n.get("clientShortname2"),
- "terms_of_use": React.renderComponentToStaticMarkup(
- React.DOM.a({href: terms_of_use_url, target: "_blank"},
+ "terms_of_use": React.renderToStaticMarkup(
+ React.createElement("a", {href: terms_of_use_url, target: "_blank"},
mozL10n.get("legal_text_tos")
)
),
- "privacy_notice": React.renderComponentToStaticMarkup(
- React.DOM.a({href: privacy_notice_url, target: "_blank"},
+ "privacy_notice": React.renderToStaticMarkup(
+ React.createElement("a", {href: privacy_notice_url, target: "_blank"},
mozL10n.get("legal_text_privacy")
)
),
});
- return React.DOM.div({id: "powered-by-wrapper"},
- React.DOM.p({id: "powered-by"},
+ return React.createElement("div", {id: "powered-by-wrapper"},
+ React.createElement("p", {id: "powered-by"},
mozL10n.get("powered_by_beforeLogo"),
- React.DOM.img({id: "powered-by-logo", className: locale}),
+ React.createElement("img", {id: "powered-by-logo", className: locale}),
mozL10n.get("powered_by_afterLogo")
),
- React.DOM.p({className: "terms-service",
+ React.createElement("p", {className: "terms-service",
dangerouslySetInnerHTML: {__html: tosHTML}})
);
} else {
- return React.DOM.div(null);
+ return React.createElement("div", null);
}
}
});
@@ -241,7 +241,7 @@
/**
* Panel settings (gear) menu entry.
*/
- var SettingsDropdownEntry = React.createClass({displayName: 'SettingsDropdownEntry',
+ var SettingsDropdownEntry = React.createClass({displayName: "SettingsDropdownEntry",
propTypes: {
onClick: React.PropTypes.func.isRequired,
label: React.PropTypes.string.isRequired,
@@ -258,11 +258,11 @@
return null;
}
return (
- React.DOM.li({onClick: this.props.onClick, className: "dropdown-menu-item"},
+ React.createElement("li", {onClick: this.props.onClick, className: "dropdown-menu-item"},
this.props.icon ?
- React.DOM.i({className: "icon icon-" + this.props.icon}) :
+ React.createElement("i", {className: "icon icon-" + this.props.icon}) :
null,
- React.DOM.span(null, this.props.label)
+ React.createElement("span", null, this.props.label)
)
);
}
@@ -271,7 +271,7 @@
/**
* Panel settings (gear) menu.
*/
- var SettingsDropdown = React.createClass({displayName: 'SettingsDropdown',
+ var SettingsDropdown = React.createClass({displayName: "SettingsDropdown",
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.WindowCloseMixin],
handleClickSettingsEntry: function() {
@@ -315,29 +315,29 @@
}
return (
- React.DOM.div({className: "settings-menu dropdown"},
- React.DOM.a({className: "button-settings", onClick: this.showDropdownMenu,
+ React.createElement("div", {className: "settings-menu dropdown"},
+ React.createElement("a", {className: "button-settings", onClick: this.showDropdownMenu,
title: mozL10n.get("settings_menu_button_tooltip")}),
- React.DOM.ul({className: cx({"dropdown-menu": true, hide: !this.state.showMenu}),
+ React.createElement("ul", {className: cx({"dropdown-menu": true, hide: !this.state.showMenu}),
onMouseLeave: this.hideDropdownMenu},
- SettingsDropdownEntry({label: mozL10n.get("settings_menu_item_settings"),
+ React.createElement(SettingsDropdownEntry, {label: mozL10n.get("settings_menu_item_settings"),
onClick: this.handleClickSettingsEntry,
displayed: false,
icon: "settings"}),
- SettingsDropdownEntry({label: mozL10n.get("settings_menu_item_account"),
+ React.createElement(SettingsDropdownEntry, {label: mozL10n.get("settings_menu_item_account"),
onClick: this.handleClickAccountEntry,
icon: "account",
displayed: this._isSignedIn()}),
- SettingsDropdownEntry({icon: "tour",
+ React.createElement(SettingsDropdownEntry, {icon: "tour",
label: mozL10n.get("tour_label"),
onClick: this.openGettingStartedTour}),
- SettingsDropdownEntry({label: this._isSignedIn() ?
+ React.createElement(SettingsDropdownEntry, {label: this._isSignedIn() ?
mozL10n.get("settings_menu_item_signout") :
mozL10n.get("settings_menu_item_signin"),
onClick: this.handleClickAuthEntry,
displayed: navigator.mozLoop.fxAEnabled,
icon: this._isSignedIn() ? "signout" : "signin"}),
- SettingsDropdownEntry({label: mozL10n.get("help_label"),
+ React.createElement(SettingsDropdownEntry, {label: mozL10n.get("help_label"),
onClick: this.handleHelpEntry,
icon: "help"})
)
@@ -349,7 +349,7 @@
/**
* Call url result view.
*/
- var CallUrlResult = React.createClass({displayName: 'CallUrlResult',
+ var CallUrlResult = React.createClass({displayName: "CallUrlResult",
mixins: [sharedMixins.DocumentVisibilityMixin],
propTypes: {
@@ -467,26 +467,26 @@
// from the react lib.
var cx = React.addons.classSet;
return (
- React.DOM.div({className: "generate-url"},
- React.DOM.header({id: "share-link-header"}, mozL10n.get("share_link_header_text")),
- React.DOM.div({className: "generate-url-stack"},
- React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
+ React.createElement("div", {className: "generate-url"},
+ React.createElement("header", {id: "share-link-header"}, mozL10n.get("share_link_header_text")),
+ React.createElement("div", {className: "generate-url-stack"},
+ React.createElement("input", {type: "url", value: this.state.callUrl, readOnly: "true",
onCopy: this.handleLinkExfiltration,
className: cx({"generate-url-input": true,
pending: this.state.pending,
// Used in functional testing, signals that
// call url was received from loop server
callUrl: !this.state.pending})}),
- React.DOM.div({className: cx({"generate-url-spinner": true,
+ React.createElement("div", {className: cx({"generate-url-spinner": true,
spinner: true,
busy: this.state.pending})})
),
- ButtonGroup({additionalClass: "url-actions"},
- Button({additionalClass: "button-email",
+ React.createElement(ButtonGroup, {additionalClass: "url-actions"},
+ React.createElement(Button, {additionalClass: "button-email",
disabled: !this.state.callUrl,
onClick: this.handleEmailButtonClick,
caption: mozL10n.get("share_button")}),
- Button({additionalClass: "button-copy",
+ React.createElement(Button, {additionalClass: "button-copy",
disabled: !this.state.callUrl,
onClick: this.handleCopyButtonClick,
caption: this.state.copied ? mozL10n.get("copied_url_button") :
@@ -500,7 +500,7 @@
/**
* FxA sign in/up link component.
*/
- var AuthLink = React.createClass({displayName: 'AuthLink',
+ var AuthLink = React.createClass({displayName: "AuthLink",
handleSignUpLinkClick: function() {
navigator.mozLoop.logInToFxA();
},
@@ -510,8 +510,8 @@
return null;
}
return (
- React.DOM.p({className: "signin-link"},
- React.DOM.a({href: "#", onClick: this.handleSignUpLinkClick},
+ React.createElement("p", {className: "signin-link"},
+ React.createElement("a", {href: "#", onClick: this.handleSignUpLinkClick},
mozL10n.get("panel_footer_signin_or_signup_link")
)
)
@@ -522,17 +522,17 @@
/**
* FxA user identity (guest/authenticated) component.
*/
- var UserIdentity = React.createClass({displayName: 'UserIdentity',
+ var UserIdentity = React.createClass({displayName: "UserIdentity",
render: function() {
return (
- React.DOM.p({className: "user-identity"},
+ React.createElement("p", {className: "user-identity"},
this.props.displayName
)
);
}
});
- var EditInPlace = React.createClass({displayName: 'EditInPlace',
+ var EditInPlace = React.createClass({displayName: "EditInPlace",
mixins: [React.addons.LinkedStateMixin],
propTypes: {
@@ -588,15 +588,15 @@
render: function() {
if (!this.state.edit) {
return (
- React.DOM.span({className: "edit-in-place", onClick: this.handleTextClick,
+ React.createElement("span", {className: "edit-in-place", onClick: this.handleTextClick,
title: mozL10n.get("rooms_name_this_room_tooltip2")},
this.state.text
)
);
}
return (
- React.DOM.form({onSubmit: this.handleFormSubmit},
- React.DOM.input({type: "text", valueLink: this.linkState("text"),
+ React.createElement("form", {onSubmit: this.handleFormSubmit},
+ React.createElement("input", {type: "text", valueLink: this.linkState("text"),
onClick: this.handleInputClick,
onBlur: this.cancelEdit})
)
@@ -607,7 +607,7 @@
/**
* Room list entry.
*/
- var RoomEntry = React.createClass({displayName: 'RoomEntry',
+ var RoomEntry = React.createClass({displayName: "RoomEntry",
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
room: React.PropTypes.instanceOf(loop.store.Room).isRequired
@@ -690,19 +690,19 @@
});
return (
- React.DOM.div({className: roomClasses, onMouseLeave: this.handleMouseLeave,
+ React.createElement("div", {className: roomClasses, onMouseLeave: this.handleMouseLeave,
onClick: this.handleClickEntry},
- React.DOM.h2(null,
- React.DOM.span({className: "room-notification"}),
- EditInPlace({text: room.roomName, onChange: this.renameRoom}),
- React.DOM.button({className: copyButtonClasses,
+ React.createElement("h2", null,
+ React.createElement("span", {className: "room-notification"}),
+ React.createElement(EditInPlace, {text: room.roomName, onChange: this.renameRoom}),
+ React.createElement("button", {className: copyButtonClasses,
title: mozL10n.get("rooms_list_copy_url_tooltip"),
onClick: this.handleCopyButtonClick}),
- React.DOM.button({className: "delete-link",
+ React.createElement("button", {className: "delete-link",
title: mozL10n.get("rooms_list_delete_tooltip"),
onClick: this.handleDeleteButtonClick})
),
- React.DOM.p(null, React.DOM.a({className: "room-url-link", href: "#"}, room.roomUrl))
+ React.createElement("p", null, React.createElement("a", {className: "room-url-link", href: "#"}, room.roomUrl))
)
);
}
@@ -711,7 +711,7 @@
/**
* Room list.
*/
- var RoomList = React.createClass({displayName: 'RoomList',
+ var RoomList = React.createClass({displayName: "RoomList",
mixins: [Backbone.Events, sharedMixins.WindowCloseMixin],
propTypes: {
@@ -776,19 +776,19 @@
}
return (
- React.DOM.div({className: "rooms"},
- React.DOM.h1(null, this._getListHeading()),
- React.DOM.div({className: "room-list"},
+ React.createElement("div", {className: "rooms"},
+ React.createElement("h1", null, this._getListHeading()),
+ React.createElement("div", {className: "room-list"},
this.state.rooms.map(function(room, i) {
- return RoomEntry({
+ return React.createElement(RoomEntry, {
key: room.roomToken,
dispatcher: this.props.dispatcher,
room: room}
);
}, this)
),
- React.DOM.p(null,
- React.DOM.button({className: "btn btn-info new-room-button",
+ React.createElement("p", null,
+ React.createElement("button", {className: "btn btn-info new-room-button",
onClick: this.handleCreateButtonClick,
disabled: this._hasPendingOperation()},
mozL10n.get("rooms_new_room_button_label")
@@ -802,7 +802,7 @@
/**
* Panel view.
*/
- var PanelView = React.createClass({displayName: 'PanelView',
+ var PanelView = React.createClass({displayName: "PanelView",
propTypes: {
notifications: React.PropTypes.object.isRequired,
client: React.PropTypes.object.isRequired,
@@ -894,23 +894,23 @@
_renderRoomsOrCallTab: function() {
if (!this._roomsEnabled()) {
return (
- Tab({name: "call"},
- React.DOM.div({className: "content-area"},
- CallUrlResult({client: this.props.client,
+ React.createElement(Tab, {name: "call"},
+ React.createElement("div", {className: "content-area"},
+ React.createElement(CallUrlResult, {client: this.props.client,
notifications: this.props.notifications,
callUrl: this.props.callUrl}),
- ToSView(null)
+ React.createElement(ToSView, null)
)
)
);
}
return (
- Tab({name: "rooms"},
- RoomList({dispatcher: this.props.dispatcher,
+ React.createElement(Tab, {name: "rooms"},
+ React.createElement(RoomList, {dispatcher: this.props.dispatcher,
store: this.props.roomStore,
userDisplayName: this._getUserDisplayName()}),
- ToSView(null)
+ React.createElement(ToSView, null)
)
);
},
@@ -950,11 +950,11 @@
if (!this.state.gettingStartedSeen) {
return (
- React.DOM.div(null,
- NotificationListView({notifications: this.props.notifications,
+ React.createElement("div", null,
+ React.createElement(NotificationListView, {notifications: this.props.notifications,
clearOnDocumentHidden: true}),
- GettingStartedView(null),
- ToSView(null)
+ React.createElement(GettingStartedView, null),
+ React.createElement(ToSView, null)
)
);
}
@@ -966,38 +966,38 @@
}
return (
- React.DOM.div(null,
- NotificationListView({notifications: this.props.notifications,
+ React.createElement("div", null,
+ React.createElement(NotificationListView, {notifications: this.props.notifications,
clearOnDocumentHidden: true}),
- TabView({ref: "tabView", selectedTab: this.props.selectedTab,
+ React.createElement(TabView, {ref: "tabView", selectedTab: this.props.selectedTab,
buttonsHidden: hideButtons},
this._renderRoomsOrCallTab(),
- Tab({name: "contacts"},
- ContactsList({selectTab: this.selectTab,
+ React.createElement(Tab, {name: "contacts"},
+ React.createElement(ContactsList, {selectTab: this.selectTab,
startForm: this.startForm})
),
- Tab({name: "contacts_add", hidden: true},
- ContactDetailsForm({ref: "contacts_add", mode: "add",
+ React.createElement(Tab, {name: "contacts_add", hidden: true},
+ React.createElement(ContactDetailsForm, {ref: "contacts_add", mode: "add",
selectTab: this.selectTab})
),
- Tab({name: "contacts_edit", hidden: true},
- ContactDetailsForm({ref: "contacts_edit", mode: "edit",
+ React.createElement(Tab, {name: "contacts_edit", hidden: true},
+ React.createElement(ContactDetailsForm, {ref: "contacts_edit", mode: "edit",
selectTab: this.selectTab})
),
- Tab({name: "contacts_import", hidden: true},
- ContactDetailsForm({ref: "contacts_import", mode: "import",
+ React.createElement(Tab, {name: "contacts_import", hidden: true},
+ React.createElement(ContactDetailsForm, {ref: "contacts_import", mode: "import",
selectTab: this.selectTab})
)
),
- React.DOM.div({className: "footer"},
- React.DOM.div({className: "user-details"},
- UserIdentity({displayName: this._getUserDisplayName()}),
- AvailabilityDropdown(null)
+ React.createElement("div", {className: "footer"},
+ React.createElement("div", {className: "user-details"},
+ React.createElement(UserIdentity, {displayName: this._getUserDisplayName()}),
+ React.createElement(AvailabilityDropdown, null)
),
- React.DOM.div({className: "signin-details"},
- AuthLink(null),
- React.DOM.div({className: "footer-signin-separator"}),
- SettingsDropdown(null)
+ React.createElement("div", {className: "signin-details"},
+ React.createElement(AuthLink, null),
+ React.createElement("div", {className: "footer-signin-separator"}),
+ React.createElement(SettingsDropdown, null)
)
)
)
@@ -1021,7 +1021,7 @@
notifications: notifications
});
- React.renderComponent(PanelView({
+ React.render(React.createElement(PanelView, {
client: client,
notifications: notifications,
roomStore: roomStore,
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/panel.jsx thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/panel.jsx
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/js/panel.jsx 2015-01-06 13:14:19.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/js/panel.jsx 2015-01-07 22:15:19.000000000 +0000
@@ -212,12 +212,12 @@
var privacy_notice_url = navigator.mozLoop.getLoopPref('legal.privacy_url');
var tosHTML = mozL10n.get("legal_text_and_links3", {
"clientShortname": mozL10n.get("clientShortname2"),
- "terms_of_use": React.renderComponentToStaticMarkup(
+ "terms_of_use": React.renderToStaticMarkup(
{mozL10n.get("legal_text_tos")}
),
- "privacy_notice": React.renderComponentToStaticMarkup(
+ "privacy_notice": React.renderToStaticMarkup(
{mozL10n.get("legal_text_privacy")}
@@ -1021,7 +1021,7 @@
notifications: notifications
});
- React.renderComponent(
-
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/js/feedbackViews.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/js/feedbackViews.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/js/feedbackViews.js 2015-01-06 13:14:20.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/js/feedbackViews.js 2015-01-07 22:15:19.000000000 +0000
@@ -25,7 +25,7 @@
* Props:
* -
*/
- var FeedbackLayout = React.createClass({displayName: 'FeedbackLayout',
+ var FeedbackLayout = React.createClass({displayName: "FeedbackLayout",
propTypes: {
children: React.PropTypes.component.isRequired,
title: React.PropTypes.string.isRequired,
@@ -33,19 +33,19 @@
},
render: function() {
- var backButton = React.DOM.div(null);
+ var backButton = React.createElement("div", null);
if (this.props.reset) {
backButton = (
- React.DOM.button({className: "fx-embedded-btn-back", type: "button",
+ React.createElement("button", {className: "fx-embedded-btn-back", type: "button",
onClick: this.props.reset},
"« ", l10n.get("feedback_back_button")
)
);
}
return (
- React.DOM.div({className: "feedback"},
+ React.createElement("div", {className: "feedback"},
backButton,
- React.DOM.h3(null, this.props.title),
+ React.createElement("h3", null, this.props.title),
this.props.children
)
);
@@ -55,7 +55,7 @@
/**
* Detailed feedback form.
*/
- var FeedbackForm = React.createClass({displayName: 'FeedbackForm',
+ var FeedbackForm = React.createClass({displayName: "FeedbackForm",
propTypes: {
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
pending: React.PropTypes.bool,
@@ -84,8 +84,8 @@
var categories = this._getCategories();
return Object.keys(categories).map(function(category, key) {
return (
- React.DOM.label({key: key, className: "feedback-category-label"},
- React.DOM.input({type: "radio", ref: "category", name: "category",
+ React.createElement("label", {key: key, className: "feedback-category-label"},
+ React.createElement("input", {type: "radio", ref: "category", name: "category",
className: "feedback-category-radio",
value: category,
onChange: this.handleCategoryChange,
@@ -149,12 +149,12 @@
var descriptionDisplayValue = this.state.category === "other" ?
this.state.description : "";
return (
- FeedbackLayout({title: l10n.get("feedback_what_makes_you_sad"),
+ React.createElement(FeedbackLayout, {title: l10n.get("feedback_what_makes_you_sad"),
reset: this.props.reset},
- React.DOM.form({onSubmit: this.handleFormSubmit},
+ React.createElement("form", {onSubmit: this.handleFormSubmit},
this._getCategoryFields(),
- React.DOM.p(null,
- React.DOM.input({type: "text", ref: "description", name: "description",
+ React.createElement("p", null,
+ React.createElement("input", {type: "text", ref: "description", name: "description",
className: "feedback-description",
onChange: this.handleDescriptionFieldChange,
onFocus: this.handleDescriptionFieldFocus,
@@ -162,7 +162,7 @@
placeholder:
l10n.get("feedback_custom_category_text_placeholder")})
),
- React.DOM.button({type: "submit", className: "btn btn-success",
+ React.createElement("button", {type: "submit", className: "btn btn-success",
disabled: !this._isFormReady()},
l10n.get("feedback_submit_button")
)
@@ -179,7 +179,7 @@
* - {Function} onAfterFeedbackReceived Function to execute after the
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
*/
- var FeedbackReceived = React.createClass({displayName: 'FeedbackReceived',
+ var FeedbackReceived = React.createClass({displayName: "FeedbackReceived",
propTypes: {
onAfterFeedbackReceived: React.PropTypes.func
},
@@ -208,8 +208,8 @@
}
}
return (
- FeedbackLayout({title: l10n.get("feedback_thank_you_heading")},
- React.DOM.p({className: "info thank-you"},
+ React.createElement(FeedbackLayout, {title: l10n.get("feedback_thank_you_heading")},
+ React.createElement("p", {className: "info thank-you"},
l10n.get("feedback_window_will_close_in2", {
countdown: this.state.countdown,
num: this.state.countdown
@@ -222,7 +222,7 @@
/**
* Feedback view.
*/
- var FeedbackView = React.createClass({displayName: 'FeedbackView',
+ var FeedbackView = React.createClass({displayName: "FeedbackView",
mixins: [Backbone.Events],
propTypes: {
@@ -283,12 +283,12 @@
default:
case FEEDBACK_STATES.INIT: {
return (
- FeedbackLayout({title:
+ React.createElement(FeedbackLayout, {title:
l10n.get("feedback_call_experience_heading2")},
- React.DOM.div({className: "faces"},
- React.DOM.button({className: "face face-happy",
+ React.createElement("div", {className: "faces"},
+ React.createElement("button", {className: "face face-happy",
onClick: this.handleHappyClick}),
- React.DOM.button({className: "face face-sad",
+ React.createElement("button", {className: "face face-sad",
onClick: this.handleSadClick})
)
)
@@ -296,7 +296,7 @@
}
case FEEDBACK_STATES.DETAILS: {
return (
- FeedbackForm({
+ React.createElement(FeedbackForm, {
feedbackStore: this.props.feedbackStore,
reset: this.reset,
pending: this.state.feedbackState === FEEDBACK_STATES.PENDING})
@@ -311,7 +311,7 @@
this.state.error);
}
return (
- FeedbackReceived({
+ React.createElement(FeedbackReceived, {
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived})
);
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js 2015-01-06 13:14:20.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js 2015-01-07 22:15:19.000000000 +0000
@@ -194,12 +194,22 @@
* https://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
*/
_onSessionDisconnected: function(event) {
- // We only need to worry about the network disconnected reason here.
- if (event.reason === "networkDisconnected") {
- this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
- reason: FAILURE_REASONS.NETWORK_DISCONNECTED
- }));
+ var reason;
+ switch (event.reason) {
+ case "networkDisconnected":
+ reason = FAILURE_REASONS.NETWORK_DISCONNECTED;
+ break;
+ case "forceDisconnected":
+ reason = FAILURE_REASONS.EXPIRED_OR_INVALID;
+ break;
+ default:
+ // Other cases don't need to be handled.
+ return;
}
+
+ this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
+ reason: reason
+ }));
},
/**
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/js/views.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/js/views.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/js/views.js 2015-01-06 13:14:20.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/js/views.js 2015-01-07 22:15:19.000000000 +0000
@@ -23,7 +23,7 @@
* - {Function} action Function to be executed on click.
* - {Enabled} enabled Stream activation status (default: true).
*/
- var MediaControlButton = React.createClass({displayName: 'MediaControlButton',
+ var MediaControlButton = React.createClass({displayName: "MediaControlButton",
propTypes: {
scope: React.PropTypes.string.isRequired,
type: React.PropTypes.string.isRequired,
@@ -64,7 +64,7 @@
render: function() {
return (
/* jshint ignore:start */
- React.DOM.button({className: this._getClasses(),
+ React.createElement("button", {className: this._getClasses(),
title: this._getTitle(),
onClick: this.handleClick})
/* jshint ignore:end */
@@ -75,7 +75,7 @@
/**
* Conversation controls.
*/
- var ConversationToolbar = React.createClass({displayName: 'ConversationToolbar',
+ var ConversationToolbar = React.createClass({displayName: "ConversationToolbar",
getDefaultProps: function() {
return {
video: {enabled: true, visible: true},
@@ -112,22 +112,22 @@
render: function() {
var cx = React.addons.classSet;
return (
- React.DOM.ul({className: "conversation-toolbar"},
- React.DOM.li({className: "conversation-toolbar-btn-box btn-hangup-entry"},
- React.DOM.button({className: "btn btn-hangup", onClick: this.handleClickHangup,
+ React.createElement("ul", {className: "conversation-toolbar"},
+ React.createElement("li", {className: "conversation-toolbar-btn-box btn-hangup-entry"},
+ React.createElement("button", {className: "btn btn-hangup", onClick: this.handleClickHangup,
title: l10n.get("hangup_button_title"),
disabled: !this.props.enableHangup},
this._getHangupButtonLabel()
)
),
- React.DOM.li({className: "conversation-toolbar-btn-box"},
- MediaControlButton({action: this.handleToggleVideo,
+ React.createElement("li", {className: "conversation-toolbar-btn-box"},
+ React.createElement(MediaControlButton, {action: this.handleToggleVideo,
enabled: this.props.video.enabled,
visible: this.props.video.visible,
scope: "local", type: "video"})
),
- React.DOM.li({className: "conversation-toolbar-btn-box"},
- MediaControlButton({action: this.handleToggleAudio,
+ React.createElement("li", {className: "conversation-toolbar-btn-box"},
+ React.createElement(MediaControlButton, {action: this.handleToggleAudio,
enabled: this.props.audio.enabled,
visible: this.props.audio.visible,
scope: "local", type: "audio"})
@@ -140,7 +140,7 @@
/**
* Conversation view.
*/
- var ConversationView = React.createClass({displayName: 'ConversationView',
+ var ConversationView = React.createClass({displayName: "ConversationView",
mixins: [Backbone.Events, sharedMixins.AudioMixin],
propTypes: {
@@ -324,15 +324,15 @@
});
/* jshint ignore:start */
return (
- React.DOM.div({className: "video-layout-wrapper"},
- React.DOM.div({className: "conversation"},
- React.DOM.div({className: "media nested"},
- React.DOM.div({className: "video_wrapper remote_wrapper"},
- React.DOM.div({className: "video_inner remote"})
+ React.createElement("div", {className: "video-layout-wrapper"},
+ React.createElement("div", {className: "conversation"},
+ React.createElement("div", {className: "media nested"},
+ React.createElement("div", {className: "video_wrapper remote_wrapper"},
+ React.createElement("div", {className: "video_inner remote"})
),
- React.DOM.div({className: localStreamClasses})
+ React.createElement("div", {className: localStreamClasses})
),
- ConversationToolbar({video: this.state.video,
+ React.createElement(ConversationToolbar, {video: this.state.video,
audio: this.state.audio,
publishStream: this.publishStream,
hangup: this.hangup})
@@ -346,7 +346,7 @@
/**
* Notification view.
*/
- var NotificationView = React.createClass({displayName: 'NotificationView',
+ var NotificationView = React.createClass({displayName: "NotificationView",
mixins: [Backbone.Events],
propTypes: {
@@ -357,19 +357,19 @@
render: function() {
var notification = this.props.notification;
return (
- React.DOM.div({className: "notificationContainer"},
- React.DOM.div({key: this.props.key,
+ React.createElement("div", {className: "notificationContainer"},
+ React.createElement("div", {key: this.props.key,
className: "alert alert-" + notification.get("level")},
- React.DOM.span({className: "message"}, notification.get("message"))
+ React.createElement("span", {className: "message"}, notification.get("message"))
),
- React.DOM.div({className: "detailsBar details-" + notification.get("level"),
+ React.createElement("div", {className: "detailsBar details-" + notification.get("level"),
hidden: !notification.get("details")},
- React.DOM.button({className: "detailsButton btn-info",
+ React.createElement("button", {className: "detailsButton btn-info",
onClick: notification.get("detailsButtonCallback"),
hidden: !notification.get("detailsButtonLabel") || !notification.get("detailsButtonCallback")},
notification.get("detailsButtonLabel")
),
- React.DOM.span({className: "details"}, notification.get("details"))
+ React.createElement("span", {className: "details"}, notification.get("details"))
)
)
);
@@ -379,7 +379,7 @@
/**
* Notification list view.
*/
- var NotificationListView = React.createClass({displayName: 'NotificationListView',
+ var NotificationListView = React.createClass({displayName: "NotificationListView",
mixins: [Backbone.Events, sharedMixins.DocumentVisibilityMixin],
propTypes: {
@@ -419,9 +419,9 @@
render: function() {
return (
- React.DOM.div({className: "messages"},
+ React.createElement("div", {className: "messages"},
this.props.notifications.map(function(notification, key) {
- return NotificationView({key: key, notification: notification});
+ return React.createElement(NotificationView, {key: key, notification: notification});
})
)
@@ -429,7 +429,7 @@
}
});
- var Button = React.createClass({displayName: 'Button',
+ var Button = React.createClass({displayName: "Button",
propTypes: {
caption: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func.isRequired,
@@ -453,18 +453,18 @@
classObject[this.props.additionalClass] = true;
}
return (
- React.DOM.button({onClick: this.props.onClick,
+ React.createElement("button", {onClick: this.props.onClick,
disabled: this.props.disabled,
id: this.props.htmlId,
className: cx(classObject)},
- React.DOM.span({className: "button-caption"}, this.props.caption),
+ React.createElement("span", {className: "button-caption"}, this.props.caption),
this.props.children
)
);
}
});
- var ButtonGroup = React.createClass({displayName: 'ButtonGroup',
+ var ButtonGroup = React.createClass({displayName: "ButtonGroup",
PropTypes: {
additionalClass: React.PropTypes.string
},
@@ -482,7 +482,7 @@
classObject[this.props.additionalClass] = true;
}
return (
- React.DOM.div({className: cx(classObject)},
+ React.createElement("div", {className: cx(classObject)},
this.props.children
)
);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/libs/react-0.11.2.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/libs/react-0.11.2.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/loop/content/shared/libs/react-0.11.2.js 2015-01-06 13:14:20.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/loop/content/shared/libs/react-0.11.2.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,20276 +0,0 @@
-/**
- * React (with addons) v0.11.2
- */
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -1;
- }
-
-};
-
-module.exports = CSSCore;
-
-},{"./invariant":134}],4:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule CSSProperty
- */
-
-"use strict";
-
-/**
- * CSS properties which accept numbers but are not in units of "px".
- */
-var isUnitlessNumber = {
- columnCount: true,
- fillOpacity: true,
- flex: true,
- flexGrow: true,
- flexShrink: true,
- fontWeight: true,
- lineClamp: true,
- lineHeight: true,
- opacity: true,
- order: true,
- orphans: true,
- widows: true,
- zIndex: true,
- zoom: true
-};
-
-/**
- * @param {string} prefix vendor-specific prefix, eg: Webkit
- * @param {string} key style name, eg: transitionDuration
- * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
- * WebkitTransitionDuration
- */
-function prefixKey(prefix, key) {
- return prefix + key.charAt(0).toUpperCase() + key.substring(1);
-}
-
-/**
- * Support style names that may come passed in prefixed by adding permutations
- * of vendor prefixes.
- */
-var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
-
-// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
-// infinite loop, because it iterates over the newly added props too.
-Object.keys(isUnitlessNumber).forEach(function(prop) {
- prefixes.forEach(function(prefix) {
- isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
- });
-});
-
-/**
- * Most style properties can be unset by doing .style[prop] = '' but IE8
- * doesn't like doing that with shorthand properties so for the properties that
- * IE8 breaks on, which are listed here, we instead unset each of the
- * individual properties. See http://bugs.jquery.com/ticket/12385.
- * The 4-value 'clock' properties like margin, padding, border-width seem to
- * behave without any problems. Curiously, list-style works too without any
- * special prodding.
- */
-var shorthandPropertyExpansions = {
- background: {
- backgroundImage: true,
- backgroundPosition: true,
- backgroundRepeat: true,
- backgroundColor: true
- },
- border: {
- borderWidth: true,
- borderStyle: true,
- borderColor: true
- },
- borderBottom: {
- borderBottomWidth: true,
- borderBottomStyle: true,
- borderBottomColor: true
- },
- borderLeft: {
- borderLeftWidth: true,
- borderLeftStyle: true,
- borderLeftColor: true
- },
- borderRight: {
- borderRightWidth: true,
- borderRightStyle: true,
- borderRightColor: true
- },
- borderTop: {
- borderTopWidth: true,
- borderTopStyle: true,
- borderTopColor: true
- },
- font: {
- fontStyle: true,
- fontVariant: true,
- fontWeight: true,
- fontSize: true,
- lineHeight: true,
- fontFamily: true
- }
-};
-
-var CSSProperty = {
- isUnitlessNumber: isUnitlessNumber,
- shorthandPropertyExpansions: shorthandPropertyExpansions
-};
-
-module.exports = CSSProperty;
-
-},{}],5:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule CSSPropertyOperations
- * @typechecks static-only
- */
-
-"use strict";
-
-var CSSProperty = _dereq_("./CSSProperty");
-
-var dangerousStyleValue = _dereq_("./dangerousStyleValue");
-var hyphenateStyleName = _dereq_("./hyphenateStyleName");
-var memoizeStringOnly = _dereq_("./memoizeStringOnly");
-
-var processStyleName = memoizeStringOnly(function(styleName) {
- return hyphenateStyleName(styleName);
-});
-
-/**
- * Operations for dealing with CSS properties.
- */
-var CSSPropertyOperations = {
-
- /**
- * Serializes a mapping of style properties for use as inline styles:
- *
- * > createMarkupForStyles({width: '200px', height: 0})
- * "width:200px;height:0;"
- *
- * Undefined values are ignored so that declarative programming is easier.
- * The result should be HTML-escaped before insertion into the DOM.
- *
- * @param {object} styles
- * @return {?string}
- */
- createMarkupForStyles: function(styles) {
- var serialized = '';
- for (var styleName in styles) {
- if (!styles.hasOwnProperty(styleName)) {
- continue;
- }
- var styleValue = styles[styleName];
- if (styleValue != null) {
- serialized += processStyleName(styleName) + ':';
- serialized += dangerousStyleValue(styleName, styleValue) + ';';
- }
- }
- return serialized || null;
- },
-
- /**
- * Sets the value for multiple styles on a node. If a value is specified as
- * '' (empty string), the corresponding style property will be unset.
- *
- * @param {DOMElement} node
- * @param {object} styles
- */
- setValueForStyles: function(node, styles) {
- var style = node.style;
- for (var styleName in styles) {
- if (!styles.hasOwnProperty(styleName)) {
- continue;
- }
- var styleValue = dangerousStyleValue(styleName, styles[styleName]);
- if (styleValue) {
- style[styleName] = styleValue;
- } else {
- var expansion = CSSProperty.shorthandPropertyExpansions[styleName];
- if (expansion) {
- // Shorthand property that IE8 won't like unsetting, so unset each
- // component to placate it
- for (var individualStyleName in expansion) {
- style[individualStyleName] = '';
- }
- } else {
- style[styleName] = '';
- }
- }
- }
- }
-
-};
-
-module.exports = CSSPropertyOperations;
-
-},{"./CSSProperty":4,"./dangerousStyleValue":115,"./hyphenateStyleName":132,"./memoizeStringOnly":143}],6:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule CallbackQueue
- */
-
-"use strict";
-
-var PooledClass = _dereq_("./PooledClass");
-
-var invariant = _dereq_("./invariant");
-var mixInto = _dereq_("./mixInto");
-
-/**
- * A specialized pseudo-event module to help keep track of components waiting to
- * be notified when their DOM representations are available for use.
- *
- * This implements `PooledClass`, so you should never need to instantiate this.
- * Instead, use `CallbackQueue.getPooled()`.
- *
- * @class ReactMountReady
- * @implements PooledClass
- * @internal
- */
-function CallbackQueue() {
- this._callbacks = null;
- this._contexts = null;
-}
-
-mixInto(CallbackQueue, {
-
- /**
- * Enqueues a callback to be invoked when `notifyAll` is invoked.
- *
- * @param {function} callback Invoked when `notifyAll` is invoked.
- * @param {?object} context Context to call `callback` with.
- * @internal
- */
- enqueue: function(callback, context) {
- this._callbacks = this._callbacks || [];
- this._contexts = this._contexts || [];
- this._callbacks.push(callback);
- this._contexts.push(context);
- },
-
- /**
- * Invokes all enqueued callbacks and clears the queue. This is invoked after
- * the DOM representation of a component has been created or updated.
- *
- * @internal
- */
- notifyAll: function() {
- var callbacks = this._callbacks;
- var contexts = this._contexts;
- if (callbacks) {
- ("production" !== "development" ? invariant(
- callbacks.length === contexts.length,
- "Mismatched list of contexts in callback queue"
- ) : invariant(callbacks.length === contexts.length));
- this._callbacks = null;
- this._contexts = null;
- for (var i = 0, l = callbacks.length; i < l; i++) {
- callbacks[i].call(contexts[i]);
- }
- callbacks.length = 0;
- contexts.length = 0;
- }
- },
-
- /**
- * Resets the internal queue.
- *
- * @internal
- */
- reset: function() {
- this._callbacks = null;
- this._contexts = null;
- },
-
- /**
- * `PooledClass` looks for this.
- */
- destructor: function() {
- this.reset();
- }
-
-});
-
-PooledClass.addPoolingTo(CallbackQueue);
-
-module.exports = CallbackQueue;
-
-},{"./PooledClass":28,"./invariant":134,"./mixInto":147}],7:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule ChangeEventPlugin
- */
-
-"use strict";
-
-var EventConstants = _dereq_("./EventConstants");
-var EventPluginHub = _dereq_("./EventPluginHub");
-var EventPropagators = _dereq_("./EventPropagators");
-var ExecutionEnvironment = _dereq_("./ExecutionEnvironment");
-var ReactUpdates = _dereq_("./ReactUpdates");
-var SyntheticEvent = _dereq_("./SyntheticEvent");
-
-var isEventSupported = _dereq_("./isEventSupported");
-var isTextInputElement = _dereq_("./isTextInputElement");
-var keyOf = _dereq_("./keyOf");
-
-var topLevelTypes = EventConstants.topLevelTypes;
-
-var eventTypes = {
- change: {
- phasedRegistrationNames: {
- bubbled: keyOf({onChange: null}),
- captured: keyOf({onChangeCapture: null})
- },
- dependencies: [
- topLevelTypes.topBlur,
- topLevelTypes.topChange,
- topLevelTypes.topClick,
- topLevelTypes.topFocus,
- topLevelTypes.topInput,
- topLevelTypes.topKeyDown,
- topLevelTypes.topKeyUp,
- topLevelTypes.topSelectionChange
- ]
- }
-};
-
-/**
- * For IE shims
- */
-var activeElement = null;
-var activeElementID = null;
-var activeElementValue = null;
-var activeElementValueProp = null;
-
-/**
- * SECTION: handle `change` event
- */
-function shouldUseChangeEvent(elem) {
- return (
- elem.nodeName === 'SELECT' ||
- (elem.nodeName === 'INPUT' && elem.type === 'file')
- );
-}
-
-var doesChangeEventBubble = false;
-if (ExecutionEnvironment.canUseDOM) {
- // See `handleChange` comment below
- doesChangeEventBubble = isEventSupported('change') && (
- !('documentMode' in document) || document.documentMode > 8
- );
-}
-
-function manualDispatchChangeEvent(nativeEvent) {
- var event = SyntheticEvent.getPooled(
- eventTypes.change,
- activeElementID,
- nativeEvent
- );
- EventPropagators.accumulateTwoPhaseDispatches(event);
-
- // If change and propertychange bubbled, we'd just bind to it like all the
- // other events and have it go through ReactBrowserEventEmitter. Since it
- // doesn't, we manually listen for the events and so we have to enqueue and
- // process the abstract event manually.
- //
- // Batching is necessary here in order to ensure that all event handlers run
- // before the next rerender (including event handlers attached to ancestor
- // elements instead of directly on the input). Without this, controlled
- // components don't work properly in conjunction with event bubbling because
- // the component is rerendered and the value reverted before all the event
- // handlers can run. See https://github.com/facebook/react/issues/708.
- ReactUpdates.batchedUpdates(runEventInBatch, event);
-}
-
-function runEventInBatch(event) {
- EventPluginHub.enqueueEvents(event);
- EventPluginHub.processEventQueue();
-}
-
-function startWatchingForChangeEventIE8(target, targetID) {
- activeElement = target;
- activeElementID = targetID;
- activeElement.attachEvent('onchange', manualDispatchChangeEvent);
-}
-
-function stopWatchingForChangeEventIE8() {
- if (!activeElement) {
- return;
- }
- activeElement.detachEvent('onchange', manualDispatchChangeEvent);
- activeElement = null;
- activeElementID = null;
-}
-
-function getTargetIDForChangeEvent(
- topLevelType,
- topLevelTarget,
- topLevelTargetID) {
- if (topLevelType === topLevelTypes.topChange) {
- return topLevelTargetID;
- }
-}
-function handleEventsForChangeEventIE8(
- topLevelType,
- topLevelTarget,
- topLevelTargetID) {
- if (topLevelType === topLevelTypes.topFocus) {
- // stopWatching() should be a noop here but we call it just in case we
- // missed a blur event somehow.
- stopWatchingForChangeEventIE8();
- startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID);
- } else if (topLevelType === topLevelTypes.topBlur) {
- stopWatchingForChangeEventIE8();
- }
-}
-
-
-/**
- * SECTION: handle `input` event
- */
-var isInputEventSupported = false;
-if (ExecutionEnvironment.canUseDOM) {
- // IE9 claims to support the input event but fails to trigger it when
- // deleting text, so we ignore its input events
- isInputEventSupported = isEventSupported('input') && (
- !('documentMode' in document) || document.documentMode > 9
- );
-}
-
-/**
- * (For old IE.) Replacement getter/setter for the `value` property that gets
- * set on the active element.
- */
-var newValueProp = {
- get: function() {
- return activeElementValueProp.get.call(this);
- },
- set: function(val) {
- // Cast to a string so we can do equality checks.
- activeElementValue = '' + val;
- activeElementValueProp.set.call(this, val);
- }
-};
-
-/**
- * (For old IE.) Starts tracking propertychange events on the passed-in element
- * and override the value property so that we can distinguish user events from
- * value changes in JS.
- */
-function startWatchingForValueChange(target, targetID) {
- activeElement = target;
- activeElementID = targetID;
- activeElementValue = target.value;
- activeElementValueProp = Object.getOwnPropertyDescriptor(
- target.constructor.prototype,
- 'value'
- );
-
- Object.defineProperty(activeElement, 'value', newValueProp);
- activeElement.attachEvent('onpropertychange', handlePropertyChange);
-}
-
-/**
- * (For old IE.) Removes the event listeners from the currently-tracked element,
- * if any exists.
- */
-function stopWatchingForValueChange() {
- if (!activeElement) {
- return;
- }
-
- // delete restores the original property definition
- delete activeElement.value;
- activeElement.detachEvent('onpropertychange', handlePropertyChange);
-
- activeElement = null;
- activeElementID = null;
- activeElementValue = null;
- activeElementValueProp = null;
-}
-
-/**
- * (For old IE.) Handles a propertychange event, sending a `change` event if
- * the value of the active element has changed.
- */
-function handlePropertyChange(nativeEvent) {
- if (nativeEvent.propertyName !== 'value') {
- return;
- }
- var value = nativeEvent.srcElement.value;
- if (value === activeElementValue) {
- return;
- }
- activeElementValue = value;
-
- manualDispatchChangeEvent(nativeEvent);
-}
-
-/**
- * If a `change` event should be fired, returns the target's ID.
- */
-function getTargetIDForInputEvent(
- topLevelType,
- topLevelTarget,
- topLevelTargetID) {
- if (topLevelType === topLevelTypes.topInput) {
- // In modern browsers (i.e., not IE8 or IE9), the input event is exactly
- // what we want so fall through here and trigger an abstract event
- return topLevelTargetID;
- }
-}
-
-// For IE8 and IE9.
-function handleEventsForInputEventIE(
- topLevelType,
- topLevelTarget,
- topLevelTargetID) {
- if (topLevelType === topLevelTypes.topFocus) {
- // In IE8, we can capture almost all .value changes by adding a
- // propertychange handler and looking for events with propertyName
- // equal to 'value'
- // In IE9, propertychange fires for most input events but is buggy and
- // doesn't fire when text is deleted, but conveniently, selectionchange
- // appears to fire in all of the remaining cases so we catch those and
- // forward the event if the value has changed
- // In either case, we don't want to call the event handler if the value
- // is changed from JS so we redefine a setter for `.value` that updates
- // our activeElementValue variable, allowing us to ignore those changes
- //
- // stopWatching() should be a noop here but we call it just in case we
- // missed a blur event somehow.
- stopWatchingForValueChange();
- startWatchingForValueChange(topLevelTarget, topLevelTargetID);
- } else if (topLevelType === topLevelTypes.topBlur) {
- stopWatchingForValueChange();
- }
-}
-
-// For IE8 and IE9.
-function getTargetIDForInputEventIE(
- topLevelType,
- topLevelTarget,
- topLevelTargetID) {
- if (topLevelType === topLevelTypes.topSelectionChange ||
- topLevelType === topLevelTypes.topKeyUp ||
- topLevelType === topLevelTypes.topKeyDown) {
- // On the selectionchange event, the target is just document which isn't
- // helpful for us so just check activeElement instead.
- //
- // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
- // propertychange on the first input event after setting `value` from a
- // script and fires only keydown, keypress, keyup. Catching keyup usually
- // gets it and catching keydown lets us fire an event for the first
- // keystroke if user does a key repeat (it'll be a little delayed: right
- // before the second keystroke). Other input methods (e.g., paste) seem to
- // fire selectionchange normally.
- if (activeElement && activeElement.value !== activeElementValue) {
- activeElementValue = activeElement.value;
- return activeElementID;
- }
- }
-}
-
-
-/**
- * SECTION: handle `click` event
- */
-function shouldUseClickEvent(elem) {
- // Use the `click` event to detect changes to checkbox and radio inputs.
- // This approach works across all browsers, whereas `change` does not fire
- // until `blur` in IE8.
- return (
- elem.nodeName === 'INPUT' &&
- (elem.type === 'checkbox' || elem.type === 'radio')
- );
-}
-
-function getTargetIDForClickEvent(
- topLevelType,
- topLevelTarget,
- topLevelTargetID) {
- if (topLevelType === topLevelTypes.topClick) {
- return topLevelTargetID;
- }
-}
-
-/**
- * This plugin creates an `onChange` event that normalizes change events
- * across form elements. This event fires at a time when it's possible to
- * change the element's value without seeing a flicker.
- *
- * Supported elements are:
- * - input (see `isTextInputElement`)
- * - textarea
- * - select
- */
-var ChangeEventPlugin = {
-
- eventTypes: eventTypes,
-
- /**
- * @param {string} topLevelType Record from `EventConstants`.
- * @param {DOMEventTarget} topLevelTarget The listening component root node.
- * @param {string} topLevelTargetID ID of `topLevelTarget`.
- * @param {object} nativeEvent Native browser event.
- * @return {*} An accumulation of synthetic events.
- * @see {EventPluginHub.extractEvents}
- */
- extractEvents: function(
- topLevelType,
- topLevelTarget,
- topLevelTargetID,
- nativeEvent) {
-
- var getTargetIDFunc, handleEventFunc;
- if (shouldUseChangeEvent(topLevelTarget)) {
- if (doesChangeEventBubble) {
- getTargetIDFunc = getTargetIDForChangeEvent;
- } else {
- handleEventFunc = handleEventsForChangeEventIE8;
- }
- } else if (isTextInputElement(topLevelTarget)) {
- if (isInputEventSupported) {
- getTargetIDFunc = getTargetIDForInputEvent;
- } else {
- getTargetIDFunc = getTargetIDForInputEventIE;
- handleEventFunc = handleEventsForInputEventIE;
- }
- } else if (shouldUseClickEvent(topLevelTarget)) {
- getTargetIDFunc = getTargetIDForClickEvent;
- }
-
- if (getTargetIDFunc) {
- var targetID = getTargetIDFunc(
- topLevelType,
- topLevelTarget,
- topLevelTargetID
- );
- if (targetID) {
- var event = SyntheticEvent.getPooled(
- eventTypes.change,
- targetID,
- nativeEvent
- );
- EventPropagators.accumulateTwoPhaseDispatches(event);
- return event;
- }
- }
-
- if (handleEventFunc) {
- handleEventFunc(
- topLevelType,
- topLevelTarget,
- topLevelTargetID
- );
- }
- }
-
-};
-
-module.exports = ChangeEventPlugin;
-
-},{"./EventConstants":16,"./EventPluginHub":18,"./EventPropagators":21,"./ExecutionEnvironment":22,"./ReactUpdates":87,"./SyntheticEvent":96,"./isEventSupported":135,"./isTextInputElement":137,"./keyOf":141}],8:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule ClientReactRootIndex
- * @typechecks
- */
-
-"use strict";
-
-var nextReactRootIndex = 0;
-
-var ClientReactRootIndex = {
- createReactRootIndex: function() {
- return nextReactRootIndex++;
- }
-};
-
-module.exports = ClientReactRootIndex;
-
-},{}],9:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule CompositionEventPlugin
- * @typechecks static-only
- */
-
-"use strict";
-
-var EventConstants = _dereq_("./EventConstants");
-var EventPropagators = _dereq_("./EventPropagators");
-var ExecutionEnvironment = _dereq_("./ExecutionEnvironment");
-var ReactInputSelection = _dereq_("./ReactInputSelection");
-var SyntheticCompositionEvent = _dereq_("./SyntheticCompositionEvent");
-
-var getTextContentAccessor = _dereq_("./getTextContentAccessor");
-var keyOf = _dereq_("./keyOf");
-
-var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
-var START_KEYCODE = 229;
-
-var useCompositionEvent = (
- ExecutionEnvironment.canUseDOM &&
- 'CompositionEvent' in window
-);
-
-// In IE9+, we have access to composition events, but the data supplied
-// by the native compositionend event may be incorrect. In Korean, for example,
-// the compositionend event contains only one character regardless of
-// how many characters have been composed since compositionstart.
-// We therefore use the fallback data while still using the native
-// events as triggers.
-var useFallbackData = (
- !useCompositionEvent ||
- (
- 'documentMode' in document &&
- document.documentMode > 8 &&
- document.documentMode <= 11
- )
-);
-
-var topLevelTypes = EventConstants.topLevelTypes;
-var currentComposition = null;
-
-// Events and their corresponding property names.
-var eventTypes = {
- compositionEnd: {
- phasedRegistrationNames: {
- bubbled: keyOf({onCompositionEnd: null}),
- captured: keyOf({onCompositionEndCapture: null})
- },
- dependencies: [
- topLevelTypes.topBlur,
- topLevelTypes.topCompositionEnd,
- topLevelTypes.topKeyDown,
- topLevelTypes.topKeyPress,
- topLevelTypes.topKeyUp,
- topLevelTypes.topMouseDown
- ]
- },
- compositionStart: {
- phasedRegistrationNames: {
- bubbled: keyOf({onCompositionStart: null}),
- captured: keyOf({onCompositionStartCapture: null})
- },
- dependencies: [
- topLevelTypes.topBlur,
- topLevelTypes.topCompositionStart,
- topLevelTypes.topKeyDown,
- topLevelTypes.topKeyPress,
- topLevelTypes.topKeyUp,
- topLevelTypes.topMouseDown
- ]
- },
- compositionUpdate: {
- phasedRegistrationNames: {
- bubbled: keyOf({onCompositionUpdate: null}),
- captured: keyOf({onCompositionUpdateCapture: null})
- },
- dependencies: [
- topLevelTypes.topBlur,
- topLevelTypes.topCompositionUpdate,
- topLevelTypes.topKeyDown,
- topLevelTypes.topKeyPress,
- topLevelTypes.topKeyUp,
- topLevelTypes.topMouseDown
- ]
- }
-};
-
-/**
- * Translate native top level events into event types.
- *
- * @param {string} topLevelType
- * @return {object}
- */
-function getCompositionEventType(topLevelType) {
- switch (topLevelType) {
- case topLevelTypes.topCompositionStart:
- return eventTypes.compositionStart;
- case topLevelTypes.topCompositionEnd:
- return eventTypes.compositionEnd;
- case topLevelTypes.topCompositionUpdate:
- return eventTypes.compositionUpdate;
- }
-}
-
-/**
- * Does our fallback best-guess model think this event signifies that
- * composition has begun?
- *
- * @param {string} topLevelType
- * @param {object} nativeEvent
- * @return {boolean}
- */
-function isFallbackStart(topLevelType, nativeEvent) {
- return (
- topLevelType === topLevelTypes.topKeyDown &&
- nativeEvent.keyCode === START_KEYCODE
- );
-}
-
-/**
- * Does our fallback mode think that this event is the end of composition?
- *
- * @param {string} topLevelType
- * @param {object} nativeEvent
- * @return {boolean}
- */
-function isFallbackEnd(topLevelType, nativeEvent) {
- switch (topLevelType) {
- case topLevelTypes.topKeyUp:
- // Command keys insert or clear IME input.
- return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1);
- case topLevelTypes.topKeyDown:
- // Expect IME keyCode on each keydown. If we get any other
- // code we must have exited earlier.
- return (nativeEvent.keyCode !== START_KEYCODE);
- case topLevelTypes.topKeyPress:
- case topLevelTypes.topMouseDown:
- case topLevelTypes.topBlur:
- // Events are not possible without cancelling IME.
- return true;
- default:
- return false;
- }
-}
-
-/**
- * Helper class stores information about selection and document state
- * so we can figure out what changed at a later date.
- *
- * @param {DOMEventTarget} root
- */
-function FallbackCompositionState(root) {
- this.root = root;
- this.startSelection = ReactInputSelection.getSelection(root);
- this.startValue = this.getText();
-}
-
-/**
- * Get current text of input.
- *
- * @return {string}
- */
-FallbackCompositionState.prototype.getText = function() {
- return this.root.value || this.root[getTextContentAccessor()];
-};
-
-/**
- * Text that has changed since the start of composition.
- *
- * @return {string}
- */
-FallbackCompositionState.prototype.getData = function() {
- var endValue = this.getText();
- var prefixLength = this.startSelection.start;
- var suffixLength = this.startValue.length - this.startSelection.end;
-
- return endValue.substr(
- prefixLength,
- endValue.length - suffixLength - prefixLength
- );
-};
-
-/**
- * This plugin creates `onCompositionStart`, `onCompositionUpdate` and
- * `onCompositionEnd` events on inputs, textareas and contentEditable
- * nodes.
- */
-var CompositionEventPlugin = {
-
- eventTypes: eventTypes,
-
- /**
- * @param {string} topLevelType Record from `EventConstants`.
- * @param {DOMEventTarget} topLevelTarget The listening component root node.
- * @param {string} topLevelTargetID ID of `topLevelTarget`.
- * @param {object} nativeEvent Native browser event.
- * @return {*} An accumulation of synthetic events.
- * @see {EventPluginHub.extractEvents}
- */
- extractEvents: function(
- topLevelType,
- topLevelTarget,
- topLevelTargetID,
- nativeEvent) {
-
- var eventType;
- var data;
-
- if (useCompositionEvent) {
- eventType = getCompositionEventType(topLevelType);
- } else if (!currentComposition) {
- if (isFallbackStart(topLevelType, nativeEvent)) {
- eventType = eventTypes.compositionStart;
- }
- } else if (isFallbackEnd(topLevelType, nativeEvent)) {
- eventType = eventTypes.compositionEnd;
- }
-
- if (useFallbackData) {
- // The current composition is stored statically and must not be
- // overwritten while composition continues.
- if (!currentComposition && eventType === eventTypes.compositionStart) {
- currentComposition = new FallbackCompositionState(topLevelTarget);
- } else if (eventType === eventTypes.compositionEnd) {
- if (currentComposition) {
- data = currentComposition.getData();
- currentComposition = null;
- }
- }
- }
-
- if (eventType) {
- var event = SyntheticCompositionEvent.getPooled(
- eventType,
- topLevelTargetID,
- nativeEvent
- );
- if (data) {
- // Inject data generated from fallback path into the synthetic event.
- // This matches the property of native CompositionEventInterface.
- event.data = data;
- }
- EventPropagators.accumulateTwoPhaseDispatches(event);
- return event;
- }
- }
-};
-
-module.exports = CompositionEventPlugin;
-
-},{"./EventConstants":16,"./EventPropagators":21,"./ExecutionEnvironment":22,"./ReactInputSelection":63,"./SyntheticCompositionEvent":94,"./getTextContentAccessor":129,"./keyOf":141}],10:[function(_dereq_,module,exports){
-/**
- * Copyright 2013-2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule DOMChildrenOperations
- * @typechecks static-only
- */
-
-"use strict";
-
-var Danger = _dereq_("./Danger");
-var ReactMultiChildUpdateTypes = _dereq_("./ReactMultiChildUpdateTypes");
-
-var getTextContentAccessor = _dereq_("./getTextContentAccessor");
-var invariant = _dereq_("./invariant");
-
-/**
- * The DOM property to use when setting text content.
- *
- * @type {string}
- * @private
- */
-var textContentAccessor = getTextContentAccessor();
-
-/**
- * Inserts `childNode` as a child of `parentNode` at the `index`.
- *
- * @param {DOMElement} parentNode Parent node in which to insert.
- * @param {DOMElement} childNode Child node to insert.
- * @param {number} index Index at which to insert the child.
- * @internal
- */
-function insertChildAt(parentNode, childNode, index) {
- // By exploiting arrays returning `undefined` for an undefined index, we can
- // rely exclusively on `insertBefore(node, null)` instead of also using
- // `appendChild(node)`. However, using `undefined` is not allowed by all
- // browsers so we must replace it with `null`.
- parentNode.insertBefore(
- childNode,
- parentNode.childNodes[index] || null
- );
-}
-
-var updateTextContent;
-if (textContentAccessor === 'textContent') {
- /**
- * Sets the text content of `node` to `text`.
- *
- * @param {DOMElement} node Node to change
- * @param {string} text New text content
- */
- updateTextContent = function(node, text) {
- node.textContent = text;
- };
-} else {
- /**
- * Sets the text content of `node` to `text`.
- *
- * @param {DOMElement} node Node to change
- * @param {string} text New text content
- */
- updateTextContent = function(node, text) {
- // In order to preserve newlines correctly, we can't use .innerText to set
- // the contents (see #1080), so we empty the element then append a text node
- while (node.firstChild) {
- node.removeChild(node.firstChild);
- }
- if (text) {
- var doc = node.ownerDocument || document;
- node.appendChild(doc.createTextNode(text));
- }
- };
-}
-
-/**
- * Operations for updating with DOM children.
- */
-var DOMChildrenOperations = {
-
- dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
-
- updateTextContent: updateTextContent,
-
- /**
- * Updates a component's children by processing a series of updates. The
- * update configurations are each expected to have a `parentNode` property.
- *
- * @param {array {
+ waitForPopupAtAnchor(popup, customizeTarget.node, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened before the popup anchored");
+ ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
+
+ // Move the info outside which should close the app menu.
+ gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
+ UITour.getTarget(window, "appMenu").then((target) => {
+ waitForPopupAtAnchor(popup, target.node, function checkPanelIsClosed() {
+ isnot(PanelUI.panel.state, "open",
+ "Panel should have closed after the info moved elsewhere.");
+ ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
+ done();
+ }, "Info should move to the appMenu button");
+ });
+ }, "Info panel should be anchored to the customize button");
+ });
+ },
+ function test_info_customize_manual_open_close(done) {
+ let popup = document.getElementById("UITourTooltip");
+ // Manually open the app menu then show an info panel there. The menu should remain open.
+ let shownPromise = promisePanelShown(window);
+ gContentAPI.showMenu("appMenu");
+ shownPromise.then(() => {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+ ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
+ gContentAPI.showInfo("customize", "Customization", "Customize me please!");
+
+ UITour.getTarget(window, "customize").then((customizeTarget) => {
+ waitForPopupAtAnchor(popup, customizeTarget.node, function checkMenuIsStillOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should still be open");
+ ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should still be set");
+
+ // Move the info outside which shouldn't close the app menu since it was manually opened.
+ gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
+ UITour.getTarget(window, "appMenu").then((target) => {
+ waitForPopupAtAnchor(popup, target.node, function checkMenuIsStillOpen() {
+ isnot(PanelUI.panel.state, "closed",
+ "Menu should remain open since UITour didn't open it in the first place");
+ waitForElementToBeHidden(window.PanelUI.panel, () => {
+ ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
+ done();
+ });
+ gContentAPI.hideMenu("appMenu");
+ }, "Info should move to the appMenu button");
+ });
+ }, "Info should be shown after showInfo() for fixed menu panel items");
+ });
+ }).then(null, Components.utils.reportError);
+ },
+ taskify(function* test_pinnedTab() {
+ is(UITour.pinnedTabs.get(window), null, "Should not already have a pinned tab");
+
+ yield addPinnedTabPromise();
+
+ let tabInfo = UITour.pinnedTabs.get(window);
+ isnot(tabInfo, null, "Should have recorded data about a pinned tab after addPinnedTab()");
+ isnot(tabInfo.tab, null, "Should have added a pinned tab after addPinnedTab()");
+ is(tabInfo.tab.pinned, true, "Tab should be marked as pinned");
+
+ let tab = tabInfo.tab;
+
+ yield removePinnedTabPromise();
+ isnot(gBrowser.tabs[0], tab, "First tab should not be the pinned tab");
+ tabInfo = UITour.pinnedTabs.get(window);
+ is(tabInfo, null, "Should not have any data about the removed pinned tab after removePinnedTab()");
+
+ yield addPinnedTabPromise();
+ yield addPinnedTabPromise();
+ yield addPinnedTabPromise();
+ is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
+ }),
+ taskify(function* test_bookmarks_menu() {
+ let bookmarksMenuButton = document.getElementById("bookmarks-menu-button");
+
+ ise(bookmarksMenuButton.open, false, "Menu should initially be closed");
+ gContentAPI.showMenu("bookmarks");
+
+ yield waitForConditionPromise(() => {
+ return bookmarksMenuButton.open;
+ }, "Menu should be visible after showMenu()");
+
+ gContentAPI.hideMenu("bookmarks");
+ yield waitForConditionPromise(() => {
+ return !bookmarksMenuButton.open;
+ }, "Menu should be hidden after hideMenu()");
+ }),
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour3.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour3.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour3.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour3.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,199 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+requestLongerTimeout(2);
+
+function test() {
+ UITourTest();
+}
+
+let tests = [
+ taskify(function* test_info_icon() {
+ let popup = document.getElementById("UITourTooltip");
+ let title = document.getElementById("UITourTooltipTitle");
+ let desc = document.getElementById("UITourTooltipDescription");
+ let icon = document.getElementById("UITourTooltipIcon");
+ let buttons = document.getElementById("UITourTooltipButtons");
+
+ // Disable the animation to prevent the mouse clicks from hitting the main
+ // window during the transition instead of the buttons in the popup.
+ popup.setAttribute("animate", "false");
+
+ yield showInfoPromise("urlbar", "a title", "some text", "image.png");
+
+ is(title.textContent, "a title", "Popup should have correct title");
+ is(desc.textContent, "some text", "Popup should have correct description text");
+
+ let imageURL = getRootDirectory(gTestPath) + "image.png";
+ imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
+ is(icon.src, imageURL, "Popup should have correct icon shown");
+
+ is(buttons.hasChildNodes(), false, "Popup should have no buttons");
+ }),
+
+ taskify(function* test_info_buttons_1() {
+ let popup = document.getElementById("UITourTooltip");
+ let title = document.getElementById("UITourTooltipTitle");
+ let desc = document.getElementById("UITourTooltipDescription");
+ let icon = document.getElementById("UITourTooltipIcon");
+
+ let buttons = gContentWindow.makeButtons();
+
+ yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
+
+ is(title.textContent, "another title", "Popup should have correct title");
+ is(desc.textContent, "moar text", "Popup should have correct description text");
+
+ let imageURL = getRootDirectory(gTestPath) + "image.png";
+ imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
+ is(icon.src, imageURL, "Popup should have correct icon shown");
+
+ buttons = document.getElementById("UITourTooltipButtons");
+ is(buttons.childElementCount, 2, "Popup should have two buttons");
+
+ is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
+ is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
+
+ is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
+ is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
+
+ let promiseHidden = promisePanelElementHidden(window, popup);
+ EventUtils.synthesizeMouseAtCenter(buttons.childNodes[0], {}, window);
+ yield promiseHidden;
+
+ ok(true, "Popup should close automatically");
+
+ yield waitForCallbackResultPromise();
+
+ is(gContentWindow.callbackResult, "button1", "Correct callback should have been called");
+ }),
+ taskify(function* test_info_buttons_2() {
+ let popup = document.getElementById("UITourTooltip");
+ let title = document.getElementById("UITourTooltipTitle");
+ let desc = document.getElementById("UITourTooltipDescription");
+ let icon = document.getElementById("UITourTooltipIcon");
+
+ let buttons = gContentWindow.makeButtons();
+
+ yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
+
+ is(title.textContent, "another title", "Popup should have correct title");
+ is(desc.textContent, "moar text", "Popup should have correct description text");
+
+ let imageURL = getRootDirectory(gTestPath) + "image.png";
+ imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
+ is(icon.src, imageURL, "Popup should have correct icon shown");
+
+ buttons = document.getElementById("UITourTooltipButtons");
+ is(buttons.childElementCount, 2, "Popup should have two buttons");
+
+ is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
+ is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
+
+ is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
+ is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
+
+ let promiseHidden = promisePanelElementHidden(window, popup);
+ EventUtils.synthesizeMouseAtCenter(buttons.childNodes[1], {}, window);
+ yield promiseHidden;
+
+ ok(true, "Popup should close automatically");
+
+ yield waitForCallbackResultPromise();
+
+ is(gContentWindow.callbackResult, "button2", "Correct callback should have been called");
+ }),
+
+ taskify(function* test_info_close_button() {
+ let popup = document.getElementById("UITourTooltip");
+ let closeButton = document.getElementById("UITourTooltipClose");
+ let infoOptions = gContentWindow.makeInfoOptions();
+
+ yield showInfoPromise("urlbar", "Close me", "X marks the spot", null, null, infoOptions);
+
+ EventUtils.synthesizeMouseAtCenter(closeButton, {}, window);
+
+ yield waitForCallbackResultPromise();
+
+ is(gContentWindow.callbackResult, "closeButton", "Close button callback called");
+ }),
+
+ taskify(function* test_info_target_callback() {
+ let popup = document.getElementById("UITourTooltip");
+ let infoOptions = gContentWindow.makeInfoOptions();
+
+ yield showInfoPromise("appMenu", "I want to know when the target is clicked", "*click*", null, null, infoOptions);
+
+ yield PanelUI.show();
+
+ yield waitForCallbackResultPromise();
+
+ is(gContentWindow.callbackResult, "target", "target callback called");
+ is(gContentWindow.callbackData.target, "appMenu", "target callback was from the appMenu");
+ is(gContentWindow.callbackData.type, "popupshown", "target callback was from the mousedown");
+
+ // Cleanup.
+ yield hideInfoPromise();
+
+ popup.removeAttribute("animate");
+ }),
+
+ function test_getConfiguration_selectedSearchEngine(done) {
+ Services.search.init(rv => {
+ ok(Components.isSuccessCode(rv), "Search service initialized");
+ let engine = Services.search.defaultEngine;
+ gContentAPI.getConfiguration("selectedSearchEngine", (data) => {
+ is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
+ done();
+ });
+ });
+ },
+
+ function test_setSearchTerm(done) {
+ const TERM = "UITour Search Term";
+ gContentAPI.setSearchTerm(TERM);
+
+ let searchbar = document.getElementById("searchbar");
+ // The UITour gets to the searchbar element through a promise, so the value setting
+ // only happens after a tick.
+ waitForCondition(() => searchbar.value == TERM, done, "Correct term set");
+ },
+
+ function test_clearSearchTerm(done) {
+ gContentAPI.setSearchTerm("");
+
+ let searchbar = document.getElementById("searchbar");
+ // The UITour gets to the searchbar element through a promise, so the value setting
+ // only happens after a tick.
+ waitForCondition(() => searchbar.value == "", done, "Search term cleared");
+ },
+
+ function test_openSearchPanel(done) {
+ let searchbar = document.getElementById("searchbar");
+
+ // If suggestions are enabled, the panel will attempt to use the network to connect
+ // to the suggestions provider, causing the test suite to fail.
+ Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.search.suggest.enabled");
+ });
+
+ ok(!searchbar.textbox.open, "Popup starts as closed");
+ gContentAPI.openSearchPanel(() => {
+ ok(searchbar.textbox.open, "Popup was opened");
+ searchbar.textbox.closePopup();
+ ok(!searchbar.textbox.open, "Popup was closed");
+ done();
+ });
+ },
+
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that width and height attributes don't get set by widget code on the highlight panel.
+ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+let highlight = document.getElementById("UITourHighlightContainer");
+let tooltip = document.getElementById("UITourTooltip");
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ UITourTest();
+}
+
+let tests = [
+ function test_highlight_size_attributes(done) {
+ gContentAPI.showHighlight("appMenu");
+ waitForElementToBeVisible(highlight, function moveTheHighlight() {
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, function checkPanelAttributes() {
+ SimpleTest.executeSoon(() => {
+ ise(highlight.height, "", "Highlight panel should have no explicit height set");
+ ise(highlight.width, "", "Highlight panel should have no explicit width set");
+ done();
+ });
+ }, "Highlight should be moved to the urlbar");
+ }, "Highlight should be shown after showHighlight() for the appMenu");
+ },
+
+ function test_info_size_attributes(done) {
+ gContentAPI.showInfo("appMenu", "test title", "test text");
+ waitForElementToBeVisible(tooltip, function moveTheTooltip() {
+ gContentAPI.showInfo("urlbar", "new title", "new text");
+ waitForElementToBeVisible(tooltip, function checkPanelAttributes() {
+ SimpleTest.executeSoon(() => {
+ ise(tooltip.height, "", "Info panel should have no explicit height set");
+ ise(tooltip.width, "", "Info panel should have no explicit width set");
+ done();
+ });
+ }, "Tooltip should be moved to the urlbar");
+ }, "Tooltip should be shown after showInfo() for the appMenu");
+ },
+
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_availableTargets.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_availableTargets.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_availableTargets.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_availableTargets.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,134 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+let hasWebIDE = Services.prefs.getBoolPref("devtools.webide.widget.enabled");
+
+function test() {
+ requestLongerTimeout(2);
+ UITourTest();
+}
+
+function searchEngineTargets() {
+ let engines = Services.search.getVisibleEngines();
+ return ["searchEngine-" + engine.identifier
+ for (engine of engines)
+ if (engine.identifier)];
+}
+
+let tests = [
+ function test_availableTargets(done) {
+ gContentAPI.getConfiguration("availableTargets", (data) => {
+ ok_targets(data, [
+ "accountStatus",
+ "addons",
+ "appMenu",
+ "backForward",
+ "bookmarks",
+ "customize",
+ "help",
+ "home",
+ "loop",
+ "devtools",
+ "pinnedTab",
+ "privateWindow",
+ "quit",
+ "search",
+ "searchIcon",
+ "urlbar",
+ ...searchEngineTargets(),
+ ...(hasWebIDE ? ["webide"] : [])
+ ]);
+
+ ok(UITour.availableTargetsCache.has(window),
+ "Targets should now be cached");
+ done();
+ });
+ },
+
+ function test_availableTargets_changeWidgets(done) {
+ CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
+ ok(!UITour.availableTargetsCache.has(window),
+ "Targets should be evicted from cache after widget change");
+ gContentAPI.getConfiguration("availableTargets", (data) => {
+ ok_targets(data, [
+ "accountStatus",
+ "addons",
+ "appMenu",
+ "backForward",
+ "customize",
+ "help",
+ "loop",
+ "devtools",
+ "home",
+ "pinnedTab",
+ "privateWindow",
+ "quit",
+ "search",
+ "searchIcon",
+ "urlbar",
+ ...searchEngineTargets(),
+ ...(hasWebIDE ? ["webide"] : [])
+ ]);
+
+ ok(UITour.availableTargetsCache.has(window),
+ "Targets should now be cached again");
+ CustomizableUI.reset();
+ ok(!UITour.availableTargetsCache.has(window),
+ "Targets should not be cached after reset");
+ done();
+ });
+ },
+
+ function test_availableTargets_exceptionFromGetTarget(done) {
+ // The query function for the "search" target will throw if it's not found.
+ // Make sure the callback still fires with the other available targets.
+ CustomizableUI.removeWidgetFromArea("search-container");
+ gContentAPI.getConfiguration("availableTargets", (data) => {
+ // Default minus "search" and "searchProvider" and "searchIcon"
+ ok_targets(data, [
+ "accountStatus",
+ "addons",
+ "appMenu",
+ "backForward",
+ "bookmarks",
+ "customize",
+ "help",
+ "home",
+ "loop",
+ "devtools",
+ "pinnedTab",
+ "privateWindow",
+ "quit",
+ "urlbar",
+ ...(hasWebIDE ? ["webide"] : [])
+ ]);
+
+ CustomizableUI.reset();
+ done();
+ });
+ },
+];
+
+function ok_targets(actualData, expectedTargets) {
+ // Depending on how soon after page load this is called, the selected tab icon
+ // may or may not be showing the loading throbber. Check for its presence and
+ // insert it into expectedTargets if it's visible.
+ let selectedTabIcon =
+ document.getAnonymousElementByAttribute(gBrowser.selectedTab,
+ "anonid",
+ "tab-icon-image");
+ if (selectedTabIcon && UITour.isElementVisible(selectedTabIcon))
+ expectedTargets.push("selectedTabIcon");
+
+ ok(Array.isArray(actualData.targets), "data.targets should be an array");
+ is(actualData.targets.sort().toString(), expectedTargets.sort().toString(),
+ "Targets should be as expected");
+}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_detach_tab.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_detach_tab.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_detach_tab.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_detach_tab.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Detaching a tab to a new window shouldn't break the menu panel.
+ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+let gContentDoc;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ registerCleanupFunction(function() {
+ gContentDoc = null;
+ });
+ UITourTest();
+}
+
+/**
+ * When tab is changed we're tearing the tour down. So the UITour client has to always be aware of this
+ * fact and therefore listens to visibilitychange events.
+ * In particular this scenario happens for detaching the tab (ie. moving it to a new window).
+ */
+let tests = [
+ taskify(function* test_move_tab_to_new_window(done) {
+ let onVisibilityChange = (aEvent) => {
+ if (!document.hidden && window != UITour.getChromeWindow(aEvent.target)) {
+ gContentAPI.showHighlight("appMenu");
+ }
+ };
+
+ let highlight = document.getElementById("UITourHighlight");
+ let windowDestroyedDeferred = Promise.defer();
+ let onDOMWindowDestroyed = (aWindow) => {
+ if (gContentWindow && aWindow == gContentWindow) {
+ Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
+ windowDestroyedDeferred.resolve();
+ }
+ };
+
+ let browserStartupDeferred = Promise.defer();
+ Services.obs.addObserver(function onBrowserDelayedStartup(aWindow) {
+ Services.obs.removeObserver(onBrowserDelayedStartup, "browser-delayed-startup-finished");
+ browserStartupDeferred.resolve(aWindow);
+ }, "browser-delayed-startup-finished", false);
+
+ // NB: we're using this rather than gContentWindow.document because the latter wouldn't
+ // have an XRayWrapper, and we need to compare this to the doc we get using this method
+ // later on...
+ gContentDoc = gBrowser.selectedBrowser.contentDocument;
+ gContentDoc.addEventListener("visibilitychange", onVisibilityChange, false);
+ gContentAPI.showHighlight("appMenu");
+
+ yield elementVisiblePromise(highlight);
+
+ gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
+
+ gContentWindow = yield browserStartupDeferred.promise;
+
+ // This highlight should be shown thanks to the visibilitychange listener.
+ let newWindowHighlight = gContentWindow.document.getElementById("UITourHighlight");
+ yield elementVisiblePromise(newWindowHighlight);
+
+ let selectedTab = gContentWindow.gBrowser.selectedTab;
+ is(selectedTab.linkedBrowser && selectedTab.linkedBrowser.contentDocument, gContentDoc, "Document should be selected in new window");
+ ok(UITour.originTabs && UITour.originTabs.has(gContentWindow), "Window should be known");
+ ok(UITour.originTabs.get(gContentWindow).has(selectedTab), "Tab should be known");
+
+ let shownPromise = promisePanelShown(gContentWindow);
+ gContentAPI.showMenu("appMenu");
+ yield shownPromise;
+
+ isnot(gContentWindow.PanelUI.panel.state, "closed", "Panel should be open");
+ ok(gContentWindow.PanelUI.contents.children.length > 0, "Panel contents should have children");
+ gContentAPI.hideHighlight();
+ gContentAPI.hideMenu("appMenu");
+ gTestTab = null;
+
+ Services.obs.addObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
+ gContentWindow.close();
+
+ yield windowDestroyedDeferred.promise;
+ }),
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,417 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ UITourTest();
+}
+
+let tests = [
+ function test_untrusted_host(done) {
+ loadUITourTestPage(function() {
+ let bookmarksMenu = document.getElementById("bookmarks-menu-button");
+ ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
+
+ gContentAPI.showMenu("bookmarks");
+ ise(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");
+
+ done();
+ }, "http://mochi.test:8888/");
+ },
+ function test_testing_host(done) {
+ // Add two testing origins intentionally surrounded by whitespace to be ignored.
+ Services.prefs.setCharPref("browser.uitour.testingOrigins",
+ "https://test1.example.com, https://test2.example.com:443 ");
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.uitour.testingOrigins");
+ });
+ function callback(result) {
+ ok(result, "Callback should be called on a testing origin");
+ done();
+ }
+
+ loadUITourTestPage(function() {
+ gContentAPI.getConfiguration("appinfo", callback);
+ }, "https://test2.example.com/");
+ },
+ function test_unsecure_host(done) {
+ loadUITourTestPage(function() {
+ let bookmarksMenu = document.getElementById("bookmarks-menu-button");
+ ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
+
+ gContentAPI.showMenu("bookmarks");
+ ise(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");
+
+ done();
+ }, "http://example.com/");
+ },
+ function test_unsecure_host_override(done) {
+ Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
+ loadUITourTestPage(function() {
+ let highlight = document.getElementById("UITourHighlight");
+ is_element_hidden(highlight, "Highlight should initially be hidden");
+
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, done, "Highlight should be shown on a unsecure host when override pref is set");
+
+ Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
+ }, "http://example.com/");
+ },
+ function test_disabled(done) {
+ Services.prefs.setBoolPref("browser.uitour.enabled", false);
+
+ let bookmarksMenu = document.getElementById("bookmarks-menu-button");
+ ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
+
+ gContentAPI.showMenu("bookmarks");
+ ise(bookmarksMenu.open, false, "Bookmark menu should not open when feature is disabled");
+
+ Services.prefs.setBoolPref("browser.uitour.enabled", true);
+ done();
+ },
+ function test_highlight(done) {
+ function test_highlight_2() {
+ let highlight = document.getElementById("UITourHighlight");
+ gContentAPI.hideHighlight();
+
+ waitForElementToBeHidden(highlight, test_highlight_3, "Highlight should be hidden after hideHighlight()");
+ }
+ function test_highlight_3() {
+ is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");
+
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, test_highlight_4, "Highlight should be shown after showHighlight()");
+ }
+ function test_highlight_4() {
+ let highlight = document.getElementById("UITourHighlight");
+ gContentAPI.showHighlight("backForward");
+ waitForElementToBeVisible(highlight, done, "Highlight should be shown after showHighlight()");
+ }
+
+ let highlight = document.getElementById("UITourHighlight");
+ is_element_hidden(highlight, "Highlight should initially be hidden");
+
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, test_highlight_2, "Highlight should be shown after showHighlight()");
+ },
+ function test_highlight_circle(done) {
+ function check_highlight_size() {
+ let panel = highlight.parentElement;
+ let anchor = panel.anchorNode;
+ let anchorRect = anchor.getBoundingClientRect();
+ info("addons target: width: " + anchorRect.width + " height: " + anchorRect.height);
+ let maxDimension = Math.round(Math.max(anchorRect.width, anchorRect.height));
+ let highlightRect = highlight.getBoundingClientRect();
+ info("highlight: width: " + highlightRect.width + " height: " + highlightRect.height);
+ is(Math.round(highlightRect.width), maxDimension, "The width of the highlight should be equal to the largest dimension of the target");
+ is(Math.round(highlightRect.height), maxDimension, "The height of the highlight should be equal to the largest dimension of the target");
+ is(Math.round(highlightRect.height), Math.round(highlightRect.width), "The height and width of the highlight should be the same to create a circle");
+ is(highlight.style.borderRadius, "100%", "The border-radius should be 100% to create a circle");
+ done();
+ }
+ let highlight = document.getElementById("UITourHighlight");
+ is_element_hidden(highlight, "Highlight should initially be hidden");
+
+ gContentAPI.showHighlight("addons");
+ waitForElementToBeVisible(highlight, check_highlight_size, "Highlight should be shown after showHighlight()");
+ },
+ function test_highlight_customize_auto_open_close(done) {
+ let highlight = document.getElementById("UITourHighlight");
+ gContentAPI.showHighlight("customize");
+ waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Move the highlight outside which should close the app menu.
+ gContentAPI.showHighlight("appMenu");
+ waitForElementToBeVisible(highlight, function checkPanelIsClosed() {
+ isnot(PanelUI.panel.state, "open",
+ "Panel should have closed after the highlight moved elsewhere.");
+ done();
+ }, "Highlight should move to the appMenu button");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+ function test_highlight_customize_manual_open_close(done) {
+ let highlight = document.getElementById("UITourHighlight");
+ // Manually open the app menu then show a highlight there. The menu should remain open.
+ let shownPromise = promisePanelShown(window);
+ gContentAPI.showMenu("appMenu");
+ shownPromise.then(() => {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+ gContentAPI.showHighlight("customize");
+
+ waitForElementToBeVisible(highlight, function checkPanelIsStillOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should still be open");
+
+ // Move the highlight outside which shouldn't close the app menu since it was manually opened.
+ gContentAPI.showHighlight("appMenu");
+ waitForElementToBeVisible(highlight, function () {
+ isnot(PanelUI.panel.state, "closed",
+ "Panel should remain open since UITour didn't open it in the first place");
+ gContentAPI.hideMenu("appMenu");
+ done();
+ }, "Highlight should move to the appMenu button");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ }).then(null, Components.utils.reportError);
+ },
+ function test_highlight_effect(done) {
+ function waitForHighlightWithEffect(highlightEl, effect, next, error) {
+ return waitForCondition(() => highlightEl.getAttribute("active") == effect,
+ next,
+ error);
+ }
+ function checkDefaultEffect() {
+ is(highlight.getAttribute("active"), "none", "The default should be no effect");
+
+ gContentAPI.showHighlight("urlbar", "none");
+ waitForHighlightWithEffect(highlight, "none", checkZoomEffect, "There should be no effect");
+ }
+ function checkZoomEffect() {
+ gContentAPI.showHighlight("urlbar", "zoom");
+ waitForHighlightWithEffect(highlight, "zoom", () => {
+ let style = window.getComputedStyle(highlight);
+ is(style.animationName, "uitour-zoom", "The animation-name should be uitour-zoom");
+ checkSameEffectOnDifferentTarget();
+ }, "There should be a zoom effect");
+ }
+ function checkSameEffectOnDifferentTarget() {
+ gContentAPI.showHighlight("appMenu", "wobble");
+ waitForHighlightWithEffect(highlight, "wobble", () => {
+ highlight.addEventListener("animationstart", function onAnimationStart(aEvent) {
+ highlight.removeEventListener("animationstart", onAnimationStart);
+ ok(true, "Animation occurred again even though the effect was the same");
+ checkRandomEffect();
+ });
+ gContentAPI.showHighlight("backForward", "wobble");
+ }, "There should be a wobble effect");
+ }
+ function checkRandomEffect() {
+ function waitForActiveHighlight(highlightEl, next, error) {
+ return waitForCondition(() => highlightEl.hasAttribute("active"),
+ next,
+ error);
+ }
+
+ gContentAPI.hideHighlight();
+ gContentAPI.showHighlight("urlbar", "random");
+ waitForActiveHighlight(highlight, () => {
+ ok(highlight.hasAttribute("active"), "The highlight should be active");
+ isnot(highlight.getAttribute("active"), "none", "A random effect other than none should have been chosen");
+ isnot(highlight.getAttribute("active"), "random", "The random effect shouldn't be 'random'");
+ isnot(UITour.highlightEffects.indexOf(highlight.getAttribute("active")), -1, "Check that a supported effect was randomly chosen");
+ done();
+ }, "There should be an active highlight with a random effect");
+ }
+
+ let highlight = document.getElementById("UITourHighlight");
+ is_element_hidden(highlight, "Highlight should initially be hidden");
+
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, checkDefaultEffect, "Highlight should be shown after showHighlight()");
+ },
+ function test_highlight_search_engine(done) {
+ let highlight = document.getElementById("UITourHighlight");
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, () => {
+
+ let searchbar = document.getElementById("searchbar");
+ if (searchbar.getAttribute("oneoffui")) {
+ done();
+ return; // The oneoffui removes the menu that's being tested here.
+ }
+
+ gContentAPI.showMenu("searchEngines", function() {
+ isnot(searchbar, null, "Should have found searchbar");
+ let searchPopup = document.getAnonymousElementByAttribute(searchbar,
+ "anonid",
+ "searchbar-popup");
+ isnot(searchPopup, null, "Should have found search popup");
+
+ function getEngineNode(identifier) {
+ let engineNode = null;
+ for (let node of searchPopup.children) {
+ if (node.engine.identifier == identifier) {
+ engineNode = node;
+ break;
+ }
+ }
+ isnot(engineNode, null, "Should have found search engine node in popup");
+ return engineNode;
+ }
+ let googleEngineNode = getEngineNode("google");
+ let bingEngineNode = getEngineNode("bing");
+
+ gContentAPI.showHighlight("searchEngine-google");
+ waitForCondition(() => googleEngineNode.getAttribute("_moz-menuactive") == "true", function() {
+ is_element_hidden(highlight, "Highlight panel should be hidden by highlighting search engine");
+
+ gContentAPI.showHighlight("searchEngine-bing");
+ waitForCondition(() => bingEngineNode.getAttribute("_moz-menuactive") == "true", function() {
+ isnot(googleEngineNode.getAttribute("_moz-menuactive"), "true", "Previous engine should no longer be highlighted");
+
+ gContentAPI.hideHighlight();
+ waitForCondition(() => bingEngineNode.getAttribute("_moz-menuactive") != "true", function() {
+ gContentAPI.hideMenu("searchEngines");
+ waitForCondition(() => searchPopup.state == "closed", function() {
+ done();
+ }, "Search dropdown should close");
+ }, "Menu item should get attribute removed");
+ }, "Menu item should get attribute to make it look active");
+ });
+ });
+ });
+ },
+ function test_highlight_effect_unsupported(done) {
+ function checkUnsupportedEffect() {
+ is(highlight.getAttribute("active"), "none", "No effect should be used when an unsupported effect is requested");
+ done();
+ }
+
+ let highlight = document.getElementById("UITourHighlight");
+ is_element_hidden(highlight, "Highlight should initially be hidden");
+
+ gContentAPI.showHighlight("urlbar", "__UNSUPPORTED__");
+ waitForElementToBeVisible(highlight, checkUnsupportedEffect, "Highlight should be shown after showHighlight()");
+ },
+ function test_info_1(done) {
+ let popup = document.getElementById("UITourTooltip");
+ let title = document.getElementById("UITourTooltipTitle");
+ let desc = document.getElementById("UITourTooltipDescription");
+ let icon = document.getElementById("UITourTooltipIcon");
+ let buttons = document.getElementById("UITourTooltipButtons");
+
+ popup.addEventListener("popupshown", function onPopupShown() {
+ popup.removeEventListener("popupshown", onPopupShown);
+ is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
+ is(title.textContent, "test title", "Popup should have correct title");
+ is(desc.textContent, "test text", "Popup should have correct description text");
+ is(icon.src, "", "Popup should have no icon");
+ is(buttons.hasChildNodes(), false, "Popup should have no buttons");
+
+ popup.addEventListener("popuphidden", function onPopupHidden() {
+ popup.removeEventListener("popuphidden", onPopupHidden);
+
+ popup.addEventListener("popupshown", function onPopupShown() {
+ popup.removeEventListener("popupshown", onPopupShown);
+ done();
+ });
+
+ gContentAPI.showInfo("urlbar", "test title", "test text");
+
+ });
+ gContentAPI.hideInfo();
+ });
+
+ gContentAPI.showInfo("urlbar", "test title", "test text");
+ },
+ taskify(function* test_info_2() {
+ let popup = document.getElementById("UITourTooltip");
+ let title = document.getElementById("UITourTooltipTitle");
+ let desc = document.getElementById("UITourTooltipDescription");
+ let icon = document.getElementById("UITourTooltipIcon");
+ let buttons = document.getElementById("UITourTooltipButtons");
+
+ yield showInfoPromise("urlbar", "urlbar title", "urlbar text");
+
+ is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
+ is(title.textContent, "urlbar title", "Popup should have correct title");
+ is(desc.textContent, "urlbar text", "Popup should have correct description text");
+ is(icon.src, "", "Popup should have no icon");
+ is(buttons.hasChildNodes(), false, "Popup should have no buttons");
+
+ yield showInfoPromise("search", "search title", "search text");
+
+ is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
+ is(title.textContent, "search title", "Popup should have correct title");
+ is(desc.textContent, "search text", "Popup should have correct description text");
+ }),
+ function test_getConfigurationVersion(done) {
+ function callback(result) {
+ let props = ["defaultUpdateChannel", "version"];
+ for (let property of props) {
+ ok(typeof(result[property]) !== undefined, "Check " + property + " isn't undefined.");
+ is(result[property], Services.appinfo[property], "Should have the same " + property + " property.");
+ }
+ done();
+ }
+
+ gContentAPI.getConfiguration("appinfo", callback);
+ },
+ function test_addToolbarButton(done) {
+ let placement = CustomizableUI.getPlacementOfWidget("panic-button");
+ is(placement, null, "default UI has panic button in the palette");
+
+ gContentAPI.getConfiguration("availableTargets", (data) => {
+ let available = (data.targets.indexOf("forget") != -1);
+ ok(!available, "Forget button should not be available by default");
+
+ gContentAPI.addNavBarWidget("forget", () => {
+ info("addNavBarWidget callback successfully called");
+
+ let placement = CustomizableUI.getPlacementOfWidget("panic-button");
+ is(placement.area, CustomizableUI.AREA_NAVBAR);
+
+ gContentAPI.getConfiguration("availableTargets", (data) => {
+ let available = (data.targets.indexOf("forget") != -1);
+ ok(available, "Forget button should now be available");
+
+ // Cleanup
+ CustomizableUI.removeWidgetFromArea("panic-button");
+ done();
+ });
+ });
+ });
+ },
+ function test_select_search_engine(done) {
+ Services.search.init(rv => {
+ if (!Components.isSuccessCode(rv)) {
+ ok(false, "search service init failed: " + rv);
+ done();
+ return;
+ }
+ let defaultEngine = Services.search.defaultEngine;
+ gContentAPI.getConfiguration("availableTargets", data => {
+ let searchEngines = data.targets.filter(t => t.startsWith("searchEngine-"));
+ let someOtherEngineID = searchEngines.filter(t => t != "searchEngine-" + defaultEngine.identifier)[0];
+ someOtherEngineID = someOtherEngineID.replace(/^searchEngine-/, "");
+
+ let observe = function (subject, topic, verb) {
+ info("browser-search-engine-modified: " + verb);
+ if (verb == "engine-current") {
+ is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
+ done();
+ }
+ };
+ Services.obs.addObserver(observe, "browser-search-engine-modified", false);
+ registerCleanupFunction(() => {
+ // Clean up
+ Services.obs.removeObserver(observe, "browser-search-engine-modified");
+ Services.search.defaultEngine = defaultEngine;
+ });
+
+ gContentAPI.setDefaultSearchEngine(someOtherEngineID);
+ });
+ });
+ },
+ function test_treatment_tag(done) {
+ gContentAPI.setTreatmentTag("foobar", "baz");
+ gContentAPI.getTreatmentTag("foobar", (data) => {
+ is(data.value, "baz", "set and retrieved treatmentTag");
+ done();
+ });
+ },
+
+ // Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
+ taskify(function* cleanupMenus() {
+ let shownPromise = promisePanelShown(window);
+ gContentAPI.showMenu("appMenu");
+ yield shownPromise;
+ }),
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_loop.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_loop.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_loop.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_loop.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,286 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+let loopButton;
+let loopPanel = document.getElementById("loop-notification-panel");
+
+Components.utils.import("resource:///modules/UITour.jsm");
+const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
+
+function test() {
+ UITourTest();
+}
+
+let tests = [
+ taskify(function* test_menu_show_hide() {
+ ise(loopButton.open, false, "Menu should initially be closed");
+ gContentAPI.showMenu("loop");
+
+ yield waitForConditionPromise(() => {
+ return loopButton.open;
+ }, "Menu should be visible after showMenu()");
+
+ ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
+ ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
+ is(loopPanel.state, "open", "The panel should be open");
+ ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
+
+ gContentAPI.hideMenu("loop");
+ yield waitForConditionPromise(() => {
+ return !loopButton.open;
+ }, "Menu should be hidden after hideMenu()");
+
+ checkLoopPanelIsHidden();
+ }),
+ // Test the menu was cleaned up in teardown.
+ taskify(function* setup_menu_cleanup() {
+ gContentAPI.showMenu("loop");
+
+ yield waitForConditionPromise(() => {
+ return loopButton.open;
+ }, "Menu should be visible after showMenu()");
+
+ // Leave it open so it gets torn down and we can test below that teardown was succesful.
+ }),
+ taskify(function* test_menu_cleanup() {
+ // Test that the open menu from above was torn down fully.
+ checkLoopPanelIsHidden();
+ }),
+ function test_availableTargets(done) {
+ gContentAPI.showMenu("loop");
+ gContentAPI.getConfiguration("availableTargets", (data) => {
+ for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
+ isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
+ }
+ done();
+ });
+ },
+ function test_hideMenuHidesAnnotations(done) {
+ let infoPanel = document.getElementById("UITourTooltip");
+ let highlightPanel = document.getElementById("UITourHighlightContainer");
+
+ gContentAPI.showMenu("loop", function menuCallback() {
+ gContentAPI.showHighlight("loop-roomList");
+ gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
+ UITour.getTarget(window, "loop-newRoom").then((target) => {
+ waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
+ isnot(loopPanel.state, "closed", "Loop panel should still be open");
+ ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
+ is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
+ is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
+
+ info("Close the loop menu and make sure the annotations inside disappear");
+ let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
+ promisePanelElementHidden(window, highlightPanel)];
+ gContentAPI.hideMenu("loop");
+ yield Promise.all(hiddenPromises);
+ isnot(infoPanel.state, "open", "Info panel should have automatically hid");
+ isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
+ done();
+ }), "Info panel should be anchored to the new room button");
+ });
+ });
+ },
+ function test_notifyLoopChatWindowOpenedClosed(done) {
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+ gContentAPI.observe((event, params) => {
+ ok(false, "No more notifications should have arrived");
+ });
+ });
+ done();
+ });
+ document.querySelector("#pinnedchats > chatbox").close();
+ });
+ LoopRooms.open("fakeTourRoom");
+ },
+ function test_notifyLoopRoomURLCopied(done) {
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
+
+ let chat = document.querySelector("#pinnedchats > chatbox");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:RoomURLCopied", "Check Loop:RoomURLCopied notification");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+ });
+ chat.close();
+ done();
+ });
+ chat.content.contentDocument.querySelector(".btn-copy").click();
+ });
+ });
+ setupFakeRoom();
+ LoopRooms.open("fakeTourRoom");
+ },
+ function test_notifyLoopRoomURLEmailed(done) {
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
+
+ let chat = document.querySelector("#pinnedchats > chatbox");
+ let composeEmailCalled = false;
+
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:RoomURLEmailed", "Check Loop:RoomURLEmailed notification");
+ ok(composeEmailCalled, "mozLoop.composeEmail should be called");
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+ });
+ chat.close();
+ done();
+ });
+
+ let chatWin = chat.content.contentWindow;
+ let oldComposeEmail = chatWin.navigator.wrappedJSObject.mozLoop.composeEmail;
+ chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = function(recipient, subject, body) {
+ ok(recipient, "composeEmail should be invoked with at least a recipient value");
+ composeEmailCalled = true;
+ chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = oldComposeEmail;
+ };
+ chatWin.document.querySelector(".btn-email").click();
+ });
+ });
+ LoopRooms.open("fakeTourRoom");
+ },
+ taskify(function* test_arrow_panel_position() {
+ ise(loopButton.open, false, "Menu should initially be closed");
+ let popup = document.getElementById("UITourTooltip");
+
+ yield showMenuPromise("loop");
+
+ let currentTarget = "loop-newRoom";
+ yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
+ is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
+
+ currentTarget = "loop-roomList";
+ yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
+ is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
+
+ currentTarget = "loop-signInUpLink";
+ yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be underneath");
+ is(popup.popupBoxObject.alignmentPosition, "after_end", "Check " + currentTarget + " position");
+ }),
+ taskify(function* test_setConfiguration() {
+ is(Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin"), false, "pref should be false but exist");
+ gContentAPI.setConfiguration("Loop:ResumeTourOnFirstJoin", true);
+
+ yield waitForConditionPromise(() => {
+ return Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin");
+ }, "Pref should change to true via setConfiguration");
+
+ Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
+ }),
+ taskify(function* test_resumeViaMenuPanel_roomClosedTabOpen() {
+ Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
+
+ // Create a fake room and then add a fake non-owner participant
+ let roomsMap = setupFakeRoom();
+ roomsMap.get("fakeTourRoom").participants = [{
+ owner: false,
+ }];
+
+ // Set the tour URL to be the current page with a different query param
+ let gettingStartedURL = gTestTab.linkedBrowser.currentURI.resolve("?gettingstarted=1");
+ Services.prefs.setCharPref("loop.gettingStarted.url", gettingStartedURL);
+
+ let observationPromise = new Promise((resolve) => {
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:IncomingConversation", "Page should have been notified about incoming conversation");
+ ise(params.conversationOpen, false, "conversationOpen should be false");
+ is(gBrowser.selectedTab, gTestTab, "The same tab should be selected");
+ resolve();
+ });
+ });
+
+ // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
+ yield showMenuPromise("loop");
+
+ yield observationPromise;
+ Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
+ }),
+ taskify(function* test_resumeViaMenuPanel_roomClosedTabClosed() {
+ Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
+
+ // Create a fake room and then add a fake non-owner participant
+ let roomsMap = setupFakeRoom();
+ roomsMap.get("fakeTourRoom").participants = [{
+ owner: false,
+ }];
+
+ // Set the tour URL to a page that's not open yet
+ Services.prefs.setCharPref("loop.gettingStarted.url", gBrowser.currentURI.prePath);
+
+ let newTabPromise = waitForConditionPromise(() => {
+ return gBrowser.currentURI.path.contains("incomingConversation=waiting");
+ }, "New tab with incomingConversation=waiting should have opened");
+
+ // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
+ yield showMenuPromise("loop");
+
+ yield newTabPromise;
+
+ yield gBrowser.removeCurrentTab();
+ Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
+ }),
+];
+
+// End tests
+
+function checkLoopPanelIsHidden() {
+ ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
+ ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
+ isnot(loopPanel.state, "open", "The panel shouldn't be open");
+ is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
+}
+
+function setupFakeRoom() {
+ let room = {};
+ for (let prop of ["roomToken", "roomName", "roomOwner", "roomUrl", "participants"])
+ room[prop] = "fakeTourRoom";
+ let roomsMap = new Map([
+ [room.roomToken, room]
+ ]);
+ LoopRooms.stubCache(roomsMap);
+ return roomsMap;
+}
+
+if (Services.prefs.getBoolPref("loop.enabled")) {
+ loopButton = window.LoopUI.toolbarButton.node;
+ // The targets to highlight only appear after getting started is launched.
+ Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
+ Services.prefs.clearUserPref("loop.gettingStarted.seen");
+ Services.prefs.clearUserPref("loop.gettingStarted.url");
+
+ // Copied from browser/components/loop/test/mochitest/head.js
+ // Remove the iframe after each test. This also avoids mochitest complaining
+ // about leaks on shutdown as we intentionally hold the iframe open for the
+ // life of the application.
+ let frameId = loopButton.getAttribute("notificationFrameId");
+ let frame = document.getElementById(frameId);
+ if (frame) {
+ frame.remove();
+ }
+
+ // Remove the stubbed rooms
+ LoopRooms.stubCache(null);
+ });
+} else {
+ ok(true, "Loop is disabled so skip the UITour Loop tests");
+ tests = [];
+}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_modalDialog.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_modalDialog.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_modalDialog.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_modalDialog.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,106 @@
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+let handleDialog;
+
+// Modified from toolkit/components/passwordmgr/test/prompt_common.js
+var didDialog;
+
+var timer; // keep in outer scope so it's not GC'd before firing
+function startCallbackTimer() {
+ didDialog = false;
+
+ // Delay before the callback twiddles the prompt.
+ const dialogDelay = 10;
+
+ // Use a timer to invoke a callback to twiddle the authentication dialog
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.init(observer, dialogDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+
+var observer = SpecialPowers.wrapCallbackObject({
+ QueryInterface : function (iid) {
+ const interfaces = [Ci.nsIObserver,
+ Ci.nsISupports, Ci.nsISupportsWeakReference];
+
+ if (!interfaces.some( function(v) { return iid.equals(v) } ))
+ throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
+ return this;
+ },
+
+ observe : function (subject, topic, data) {
+ var doc = getDialogDoc();
+ if (doc)
+ handleDialog(doc);
+ else
+ startCallbackTimer(); // try again in a bit
+ }
+});
+
+function getDialogDoc() {
+ // Find the which contains notifyWindow, by looking
+ // through all the open windows and all the in each.
+ var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator);
+ //var enumerator = wm.getEnumerator("navigator:browser");
+ var enumerator = wm.getXULWindowEnumerator(null);
+
+ while (enumerator.hasMoreElements()) {
+ var win = enumerator.getNext();
+ var windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
+
+ var containedDocShells = windowDocShell.getDocShellEnumerator(
+ Ci.nsIDocShellTreeItem.typeChrome,
+ Ci.nsIDocShell.ENUMERATE_FORWARDS);
+ while (containedDocShells.hasMoreElements()) {
+ // Get the corresponding document for this docshell
+ var childDocShell = containedDocShells.getNext();
+ // We don't want it if it's not done loading.
+ if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
+ continue;
+ var childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
+ .contentViewer
+ .DOMDocument;
+
+ //ok(true, "Got window: " + childDoc.location.href);
+ if (childDoc.location.href == "chrome://global/content/commonDialog.xul")
+ return childDoc;
+ }
+ }
+
+ return null;
+}
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ UITourTest();
+}
+
+
+let tests = [
+ taskify(function* test_modal_dialog_while_opening_tooltip(done) {
+ let panelShown;
+ let popup;
+
+ handleDialog = (doc) => {
+ popup = document.getElementById("UITourTooltip");
+ gContentAPI.showInfo("appMenu", "test title", "test text");
+ doc.defaultView.setTimeout(function() {
+ is(popup.state, "closed", "Popup shouldn't be shown while dialog is up");
+ panelShown = promisePanelElementShown(window, popup);
+ let dialog = doc.getElementById("commonDialog");
+ dialog.acceptDialog();
+ }, 1000);
+ };
+ startCallbackTimer();
+ executeSoon(() => alert("test"));
+ yield waitForConditionPromise(() => panelShown, "Timed out waiting for panel promise to be assigned", 100);
+ yield panelShown;
+
+ yield hideInfoPromise();
+ })
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_observe.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_observe.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_observe.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_observe.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,51 @@
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ requestLongerTimeout(2);
+ UITourTest();
+}
+
+let tests = [
+ function test_no_params(done) {
+ function listener(event, params) {
+ is(event, "test-event-1", "Correct event name");
+ is(params, null, "No param object");
+ gContentAPI.observe(null);
+ done();
+ }
+
+ gContentAPI.observe(listener, () => {
+ UITour.notify("test-event-1");
+ });
+ },
+ function test_param_string(done) {
+ function listener(event, params) {
+ is(event, "test-event-2", "Correct event name");
+ is(params, "a param", "Correct param string");
+ gContentAPI.observe(null);
+ done();
+ }
+
+ gContentAPI.observe(listener, () => {
+ UITour.notify("test-event-2", "a param");
+ });
+ },
+ function test_param_object(done) {
+ function listener(event, params) {
+ is(event, "test-event-3", "Correct event name");
+ is(JSON.stringify(params), JSON.stringify({key: "something"}), "Correct param object");
+ gContentAPI.observe(null);
+ done();
+ }
+
+ gContentAPI.observe(listener, () => {
+ UITour.notify("test-event-3", {key: "something"});
+ });
+ },
+];
\ No newline at end of file
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_panel_close_annotation.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_panel_close_annotation.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_panel_close_annotation.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_panel_close_annotation.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,155 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that annotations disappear when their target is hidden.
+ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+let highlight = document.getElementById("UITourHighlight");
+let tooltip = document.getElementById("UITourTooltip");
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ registerCleanupFunction(() => {
+ // Close the find bar in case it's open in the remaining tab
+ gBrowser.getFindBar(gBrowser.selectedTab).close();
+ });
+ UITourTest();
+}
+
+let tests = [
+ function test_highlight_move_outside_panel(done) {
+ gContentAPI.showInfo("urlbar", "test title", "test text");
+ gContentAPI.showHighlight("customize");
+ waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Move the highlight outside which should close the app menu.
+ gContentAPI.showHighlight("appMenu");
+ waitForPopupAtAnchor(highlight.parentElement, document.getElementById("PanelUI-button"), () => {
+ isnot(PanelUI.panel.state, "open",
+ "Panel should have closed after the highlight moved elsewhere.");
+ ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
+ done();
+ }, "Highlight should move to the appMenu button and still be visible");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+
+ function test_highlight_panel_hideMenu(done) {
+ gContentAPI.showHighlight("customize");
+ gContentAPI.showInfo("search", "test title", "test text");
+ waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Close the app menu and make sure the highlight also disappeared.
+ gContentAPI.hideMenu("appMenu");
+ waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
+ isnot(PanelUI.panel.state, "open",
+ "Panel still should have closed");
+ ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
+ done();
+ }, "Highlight should have disappeared when panel closed");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+
+ function test_highlight_panel_click_find(done) {
+ gContentAPI.showHighlight("help");
+ gContentAPI.showInfo("searchIcon", "test title", "test text");
+ waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Click the find button which should close the panel.
+ let findButton = document.getElementById("find-button");
+ EventUtils.synthesizeMouseAtCenter(findButton, {});
+ waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
+ isnot(PanelUI.panel.state, "open",
+ "Panel should have closed when the find bar opened");
+ ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
+ done();
+ }, "Highlight should have disappeared when panel closed");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+
+ function test_highlight_info_panel_click_find(done) {
+ gContentAPI.showHighlight("help");
+ gContentAPI.showInfo("customize", "customize me!", "awesome!");
+ waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Click the find button which should close the panel.
+ let findButton = document.getElementById("find-button");
+ EventUtils.synthesizeMouseAtCenter(findButton, {});
+ waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
+ isnot(PanelUI.panel.state, "open",
+ "Panel should have closed when the find bar opened");
+ waitForElementToBeHidden(tooltip, function checkTooltipIsClosed() {
+ isnot(tooltip.state, "open", "The info panel should have closed too");
+ done();
+ }, "Tooltip should hide with the menu");
+ }, "Highlight should have disappeared when panel closed");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+
+ function test_highlight_panel_open_subview(done) {
+ gContentAPI.showHighlight("customize");
+ gContentAPI.showInfo("backForward", "test title", "test text");
+ waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Click the help button which should open the subview in the panel menu.
+ let helpButton = document.getElementById("PanelUI-help");
+ EventUtils.synthesizeMouseAtCenter(helpButton, {});
+ waitForElementToBeHidden(highlight, function highlightHidden() {
+ is(PanelUI.panel.state, "open",
+ "Panel should have stayed open when the subview opened");
+ ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
+ PanelUI.hide();
+ done();
+ }, "Highlight should have disappeared when the subview opened");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+
+ function test_info_panel_open_subview(done) {
+ gContentAPI.showHighlight("urlbar");
+ gContentAPI.showInfo("customize", "customize me!", "Open a subview");
+ waitForElementToBeVisible(tooltip, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Click the help button which should open the subview in the panel menu.
+ let helpButton = document.getElementById("PanelUI-help");
+ EventUtils.synthesizeMouseAtCenter(helpButton, {});
+ waitForElementToBeHidden(tooltip, function tooltipHidden() {
+ is(PanelUI.panel.state, "open",
+ "Panel should have stayed open when the subview opened");
+ is(highlight.parentElement.state, "open", "The highlight should have remained open");
+ PanelUI.hide();
+ done();
+ }, "Tooltip should have disappeared when the subview opened");
+ }, "Highlight should be shown after showHighlight() for fixed panel items");
+ },
+
+ function test_info_move_outside_panel(done) {
+ gContentAPI.showInfo("addons", "test title", "test text");
+ gContentAPI.showHighlight("urlbar");
+ let addonsButton = document.getElementById("add-ons-button");
+ waitForPopupAtAnchor(tooltip, addonsButton, function checkPanelIsOpen() {
+ isnot(PanelUI.panel.state, "closed", "Panel should have opened");
+
+ // Move the info panel outside which should close the app menu.
+ gContentAPI.showInfo("appMenu", "Cool menu button", "It's three lines");
+ waitForPopupAtAnchor(tooltip, document.getElementById("PanelUI-button"), () => {
+ isnot(PanelUI.panel.state, "open",
+ "Menu should have closed after the highlight moved elsewhere.");
+ is(highlight.parentElement.state, "open", "The highlight should have remained visible");
+ done();
+ }, "Tooltip should move to the appMenu button and still be visible");
+ }, "Tooltip should be shown after showInfo() for a panel item");
+ },
+
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_registerPageID.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_registerPageID.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_registerPageID.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_registerPageID.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+Components.utils.import("resource://gre/modules/UITelemetry.jsm");
+Components.utils.import("resource:///modules/BrowserUITelemetry.jsm");
+
+function test() {
+ UITelemetry._enabled = true;
+
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.uitour.seenPageIDs");
+ resetSeenPageIDsLazyGetter();
+ UITelemetry._enabled = undefined;
+ BrowserUITelemetry.setBucket(null);
+ delete window.UITelemetry;
+ delete window.BrowserUITelemetry;
+ });
+ UITourTest();
+}
+
+function resetSeenPageIDsLazyGetter() {
+ delete UITour.seenPageIDs;
+ // This should be kept in sync with how UITour.init() sets this.
+ Object.defineProperty(UITour, "seenPageIDs", {
+ get: UITour.restoreSeenPageIDs.bind(UITour),
+ configurable: true,
+ });
+}
+
+function checkExpectedSeenPageIDs(expected) {
+ is(UITour.seenPageIDs.size, expected.length, "Should be " + expected.length + " total seen page IDs");
+
+ for (let id of expected)
+ ok(UITour.seenPageIDs.has(id), "Should have seen '" + id + "' page ID");
+
+ let prefData = Services.prefs.getCharPref("browser.uitour.seenPageIDs");
+ prefData = new Map(JSON.parse(prefData));
+
+ is(prefData.size, expected.length, "Should be " + expected.length + " total seen page IDs persisted");
+
+ for (let id of expected)
+ ok(prefData.has(id), "Should have seen '" + id + "' page ID persisted");
+}
+
+let tests = [
+ function test_seenPageIDs_restore(done) {
+ info("Setting up seenPageIDs to be restored from pref");
+ let data = JSON.stringify([
+ ["savedID1", { lastSeen: Date.now() }],
+ ["savedID2", { lastSeen: Date.now() }],
+ // 9 weeks ago, should auto expire.
+ ["savedID3", { lastSeen: Date.now() - 9 * 7 * 24 * 60 * 60 * 1000 }],
+ ]);
+ Services.prefs.setCharPref("browser.uitour.seenPageIDs",
+ data);
+
+ resetSeenPageIDsLazyGetter();
+ checkExpectedSeenPageIDs(["savedID1", "savedID2"]);
+
+ done();
+ },
+ taskify(function* test_seenPageIDs_set_1() {
+ gContentAPI.registerPageID("testpage1");
+
+ yield waitForConditionPromise(() => UITour.seenPageIDs.size == 3, "Waiting for page to be registered.");
+
+ checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1"]);
+
+ const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
+ const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
+
+ let bucket = PREFIX + "UITour" + SEP + "testpage1";
+ is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
+
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+ bucket = PREFIX + "UITour" + SEP + "testpage1" + SEP + "inactive" + SEP + "1m";
+ is(BrowserUITelemetry.currentBucket, bucket,
+ "After switching tabs, bucket should be expiring");
+
+ gBrowser.removeTab(gBrowser.selectedTab);
+ gBrowser.selectedTab = gTestTab;
+ BrowserUITelemetry.setBucket(null);
+ }),
+ taskify(function* test_seenPageIDs_set_2() {
+ gContentAPI.registerPageID("testpage2");
+
+ yield waitForConditionPromise(() => UITour.seenPageIDs.size == 4, "Waiting for page to be registered.");
+
+ checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1", "testpage2"]);
+
+ const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
+ const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
+
+ let bucket = PREFIX + "UITour" + SEP + "testpage2";
+ is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
+
+ gBrowser.removeTab(gTestTab);
+ gTestTab = null;
+ bucket = PREFIX + "UITour" + SEP + "testpage2" + SEP + "closed" + SEP + "1m";
+ is(BrowserUITelemetry.currentBucket, bucket,
+ "After closing tab, bucket should be expiring");
+
+ BrowserUITelemetry.setBucket(null);
+ }),
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_resetProfile.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_resetProfile.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_resetProfile.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_resetProfile.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ UITourTest();
+}
+
+let tests = [
+ // Test that a reset profile dialog appears when "resetFirefox" event is triggered
+ function test_resetFirefox(done) {
+ let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ winWatcher.registerNotification(function onOpen(subj, topic, data) {
+ if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+ subj.addEventListener("load", function onLoad() {
+ subj.removeEventListener("load", onLoad);
+ if (subj.document.documentURI ==
+ "chrome://global/content/resetProfile.xul") {
+ winWatcher.unregisterNotification(onOpen);
+ ok(true, "Observed search manager window open");
+ is(subj.opener, window,
+ "Reset Firefox event opened a reset profile window.");
+ subj.close();
+ done();
+ }
+ });
+ }
+ });
+ gContentAPI.resetFirefox();
+ },
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_sync.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_sync.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/browser_UITour_sync.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/browser_UITour_sync.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("services.sync.username");
+ });
+ UITourTest();
+}
+
+let tests = [
+ function test_checkSyncSetup_disabled(done) {
+ function callback(result) {
+ is(result.setup, false, "Sync shouldn't be setup by default");
+ done();
+ }
+
+ gContentAPI.getConfiguration("sync", callback);
+ },
+
+ function test_checkSyncSetup_enabled(done) {
+ function callback(result) {
+ is(result.setup, true, "Sync should be setup");
+ done();
+ }
+
+ Services.prefs.setCharPref("services.sync.username", "uitour@tests.mozilla.org");
+ gContentAPI.getConfiguration("sync", callback);
+ },
+
+ // The showFirefoxAccounts API is sync related, so we test that here too...
+ function test_firefoxAccounts(done) {
+ // This test will load about:accounts, and we don't want that to hit the
+ // network.
+ Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri",
+ "https://example.com/");
+
+ loadUITourTestPage(function(contentWindow) {
+ let tabBrowser = gBrowser.selectedBrowser;
+ // This command will replace the current tab - so add a load event
+ // handler which will fire when that happens.
+ tabBrowser.addEventListener("load", function onload(evt) {
+ tabBrowser.removeEventListener("load", onload, true);
+
+ ise(tabBrowser.contentDocument.location.href,
+ "about:accounts?action=signup&entrypoint=uitour",
+ "about:accounts should have replaced the tab");
+
+ // the iframe in about:accounts will still be loading, so we stop
+ // that before resetting the pref.
+ tabBrowser.contentDocument.location.href = "about:blank";
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
+ done();
+ }, true);
+
+ gContentAPI.showFirefoxAccounts();
+ });
+ },
+];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/head.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/head.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/head.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/head.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,274 @@
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UITour",
+ "resource:///modules/UITour.jsm");
+
+
+const SINGLE_TRY_TIMEOUT = 100;
+const NUMBER_OF_TRIES = 30;
+
+function waitForConditionPromise(condition, timeoutMsg, tryCount=NUMBER_OF_TRIES) {
+ let defer = Promise.defer();
+ let tries = 0;
+ function checkCondition() {
+ if (tries >= tryCount) {
+ defer.reject(timeoutMsg);
+ }
+ var conditionPassed;
+ try {
+ conditionPassed = condition();
+ } catch (e) {
+ return defer.reject(e);
+ }
+ if (conditionPassed) {
+ return defer.resolve();
+ }
+ tries++;
+ setTimeout(checkCondition, SINGLE_TRY_TIMEOUT);
+ }
+ setTimeout(checkCondition, SINGLE_TRY_TIMEOUT);
+ return defer.promise;
+}
+
+function waitForCondition(condition, nextTest, errorMsg) {
+ waitForConditionPromise(condition, errorMsg).then(nextTest, (reason) => {
+ ok(false, reason + (reason.stack ? "\n" + e.stack : ""));
+ });
+}
+
+/**
+ * Wrapper to partially transition tests to Task.
+ */
+function taskify(fun) {
+ return (done) => {
+ return Task.spawn(fun).then(done, (reason) => {
+ ok(false, reason);
+ done();
+ });
+ }
+}
+
+function is_hidden(element) {
+ var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
+ if (style.display == "none")
+ return true;
+ if (style.visibility != "visible")
+ return true;
+ if (style.display == "-moz-popup")
+ return ["hiding","closed"].indexOf(element.state) != -1;
+
+ // Hiding a parent element will hide all its children
+ if (element.parentNode != element.ownerDocument)
+ return is_hidden(element.parentNode);
+
+ return false;
+}
+
+function is_visible(element) {
+ var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
+ if (style.display == "none")
+ return false;
+ if (style.visibility != "visible")
+ return false;
+ if (style.display == "-moz-popup" && element.state != "open")
+ return false;
+
+ // Hiding a parent element will hide all its children
+ if (element.parentNode != element.ownerDocument)
+ return is_visible(element.parentNode);
+
+ return true;
+}
+
+function is_element_visible(element, msg) {
+ isnot(element, null, "Element should not be null, when checking visibility");
+ ok(is_visible(element), msg);
+}
+
+function waitForElementToBeVisible(element, nextTest, msg) {
+ waitForCondition(() => is_visible(element),
+ () => {
+ ok(true, msg);
+ nextTest();
+ },
+ "Timeout waiting for visibility: " + msg);
+}
+
+function waitForElementToBeHidden(element, nextTest, msg) {
+ waitForCondition(() => is_hidden(element),
+ () => {
+ ok(true, msg);
+ nextTest();
+ },
+ "Timeout waiting for invisibility: " + msg);
+}
+
+function elementVisiblePromise(element, msg) {
+ return waitForConditionPromise(() => is_visible(element), "Timeout waiting for visibility: " + msg);
+}
+
+function elementHiddenPromise(element, msg) {
+ return waitForConditionPromise(() => is_hidden(element), "Timeout waiting for invisibility: " + msg);
+}
+
+function waitForPopupAtAnchor(popup, anchorNode, nextTest, msg) {
+ waitForCondition(() => is_visible(popup) && popup.popupBoxObject.anchorNode == anchorNode,
+ () => {
+ ok(true, msg);
+ is_element_visible(popup, "Popup should be visible");
+ nextTest();
+ },
+ "Timeout waiting for popup at anchor: " + msg);
+}
+
+function hideInfoPromise(...args) {
+ let popup = document.getElementById("UITourTooltip");
+ gContentAPI.hideInfo.apply(gContentAPI, args);
+ return promisePanelElementHidden(window, popup);
+}
+
+function showInfoPromise(...args) {
+ let popup = document.getElementById("UITourTooltip");
+ gContentAPI.showInfo.apply(gContentAPI, args);
+ return promisePanelElementShown(window, popup);
+}
+
+function showMenuPromise(name) {
+ return new Promise(resolve => {
+ gContentAPI.showMenu(name, () => resolve());
+ });
+}
+
+function waitForCallbackResultPromise() {
+ return waitForConditionPromise(() => {
+ return gContentWindow.callbackResult;
+ }, "callback should be called");
+}
+
+function addPinnedTabPromise() {
+ gContentAPI.addPinnedTab();
+ return waitForConditionPromise(() => {
+ let tabInfo = UITour.pinnedTabs.get(window);
+ if (!tabInfo) {
+ return false;
+ }
+ return tabInfo.tab.pinned;
+ });
+}
+
+function removePinnedTabPromise() {
+ gContentAPI.removePinnedTab();
+ return waitForConditionPromise(() => {
+ let tabInfo = UITour.pinnedTabs.get(window);
+ return tabInfo == null;
+ });
+}
+
+function promisePanelShown(win) {
+ let panelEl = win.PanelUI.panel;
+ return promisePanelElementShown(win, panelEl);
+}
+
+function promisePanelElementEvent(win, aPanel, aEvent) {
+ let deferred = Promise.defer();
+ let timeoutId = win.setTimeout(() => {
+ deferred.reject("Panel did not show within 5 seconds.");
+ }, 5000);
+ aPanel.addEventListener(aEvent, function onPanelEvent(e) {
+ aPanel.removeEventListener(aEvent, onPanelEvent);
+ win.clearTimeout(timeoutId);
+ deferred.resolve();
+ });
+ return deferred.promise;
+}
+
+function promisePanelElementShown(win, aPanel) {
+ return promisePanelElementEvent(win, aPanel, "popupshown");
+}
+
+function promisePanelElementHidden(win, aPanel) {
+ return promisePanelElementEvent(win, aPanel, "popuphidden");
+}
+
+function is_element_hidden(element, msg) {
+ isnot(element, null, "Element should not be null, when checking visibility");
+ ok(is_hidden(element), msg);
+}
+
+function loadUITourTestPage(callback, host = "https://example.com/") {
+ if (gTestTab)
+ gBrowser.removeTab(gTestTab);
+
+ let url = getRootDirectory(gTestPath) + "uitour.html";
+ url = url.replace("chrome://mochitests/content/", host);
+
+ gTestTab = gBrowser.addTab(url);
+ gBrowser.selectedTab = gTestTab;
+
+ gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
+ gTestTab.linkedBrowser.removeEventListener("load", onLoad, true);
+
+ gContentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
+ gContentAPI = gContentWindow.Mozilla.UITour;
+
+ waitForFocus(callback, gContentWindow);
+ }, true);
+}
+
+function UITourTest() {
+ Services.prefs.setBoolPref("browser.uitour.enabled", true);
+ let testUri = Services.io.newURI("http://example.com", null, null);
+ Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
+
+ waitForExplicitFinish();
+
+ registerCleanupFunction(function() {
+ delete window.UITour;
+ delete window.UITourMetricsProvider;
+ delete window.gContentWindow;
+ delete window.gContentAPI;
+ if (gTestTab)
+ gBrowser.removeTab(gTestTab);
+ delete window.gTestTab;
+ Services.prefs.clearUserPref("browser.uitour.enabled", true);
+ Services.perms.remove("example.com", "uitour");
+ });
+
+ function done() {
+ executeSoon(() => {
+ if (gTestTab)
+ gBrowser.removeTab(gTestTab);
+ gTestTab = null;
+
+ let highlight = document.getElementById("UITourHighlightContainer");
+ is_element_hidden(highlight, "Highlight should be closed/hidden after UITour tab is closed");
+
+ let tooltip = document.getElementById("UITourTooltip");
+ is_element_hidden(tooltip, "Tooltip should be closed/hidden after UITour tab is closed");
+
+ ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up");
+ ok(!PanelUI.panel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
+ isnot(PanelUI.panel.state, "open", "The panel shouldn't be open");
+ is(document.getElementById("PanelUI-menu-button").hasAttribute("open"), false, "Menu button should know that the menu is closed");
+
+ is(UITour.pinnedTabs.get(window), null, "Any pinned tab should be closed after UITour tab is closed");
+
+ executeSoon(nextTest);
+ });
+ }
+
+ function nextTest() {
+ if (tests.length == 0) {
+ finish();
+ return;
+ }
+ let test = tests.shift();
+ info("Starting " + test.name);
+ waitForFocus(function() {
+ loadUITourTestPage(function() {
+ test(done);
+ });
+ });
+ }
+ nextTest();
+}
Binary files /tmp/ZALrx69OUO/thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/image.png and /tmp/O2YioUdL7r/thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/image.png differ
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/uitour.html thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/uitour.html
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/test/uitour.html 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/test/uitour.html 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,39 @@
+
+
+
+
+ UITour test
+
+
+
+
+
UITour tests
+
Because Firefox is...
+
Never gonna let you down
+
Never gonna give you up
+
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/UITour.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/UITour.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/UITour.jsm 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/UITour.jsm 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,1811 @@
+// 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";
+
+this.EXPORTED_SYMBOLS = ["UITour", "UITourMetricsProvider"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
+ "resource://gre/modules/LightweightThemeManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
+ "resource://gre/modules/ResetProfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
+ "resource:///modules/CustomizableUI.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
+ "resource://gre/modules/UITelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
+ "resource:///modules/BrowserUITelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
+ "resource://gre/modules/Metrics.jsm");
+
+// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
+const PREF_LOG_LEVEL = "browser.uitour.loglevel";
+const PREF_SEENPAGEIDS = "browser.uitour.seenPageIDs";
+const MAX_BUTTONS = 4;
+
+const BUCKET_NAME = "UITour";
+const BUCKET_TIMESTEPS = [
+ 1 * 60 * 1000, // Until 1 minute after tab is closed/inactive.
+ 3 * 60 * 1000, // Until 3 minutes after tab is closed/inactive.
+ 10 * 60 * 1000, // Until 10 minutes after tab is closed/inactive.
+ 60 * 60 * 1000, // Until 1 hour after tab is closed/inactive.
+];
+
+// Time after which seen Page IDs expire.
+const SEENPAGEID_EXPIRY = 8 * 7 * 24 * 60 * 60 * 1000; // 8 weeks.
+
+// Prefix for any target matching a search engine.
+const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
+
+// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
+XPCOMUtils.defineLazyGetter(this, "log", () => {
+ let ConsoleAPI = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).ConsoleAPI;
+ let consoleOptions = {
+ // toLowerCase is because the loglevel values use title case to be compatible with Log.jsm.
+ maxLogLevel: Services.prefs.getCharPref(PREF_LOG_LEVEL).toLowerCase(),
+ prefix: "UITour",
+ };
+ return new ConsoleAPI(consoleOptions);
+});
+
+this.UITour = {
+ url: null,
+ seenPageIDs: null,
+ pageIDSourceTabs: new WeakMap(),
+ pageIDSourceWindows: new WeakMap(),
+ /* Map from browser windows to a set of tabs in which a tour is open */
+ originTabs: new WeakMap(),
+ /* Map from browser windows to a set of pinned tabs opened by (a) tour(s) */
+ pinnedTabs: new WeakMap(),
+ urlbarCapture: new WeakMap(),
+ appMenuOpenForAnnotation: new Set(),
+ availableTargetsCache: new WeakMap(),
+
+ _detachingTab: false,
+ _annotationPanelMutationObservers: new WeakMap(),
+ _queuedEvents: [],
+ _pendingDoc: null,
+
+ highlightEffects: ["random", "wobble", "zoom", "color"],
+ targets: new Map([
+ ["accountStatus", {
+ query: (aDocument) => {
+ let statusButton = aDocument.getElementById("PanelUI-fxa-status");
+ return aDocument.getAnonymousElementByAttribute(statusButton,
+ "class",
+ "toolbarbutton-icon");
+ },
+ widgetName: "PanelUI-fxa-status",
+ }],
+ ["addons", {query: "#add-ons-button"}],
+ ["appMenu", {
+ addTargetListener: (aDocument, aCallback) => {
+ let panelPopup = aDocument.getElementById("PanelUI-popup");
+ panelPopup.addEventListener("popupshown", aCallback);
+ },
+ query: "#PanelUI-button",
+ removeTargetListener: (aDocument, aCallback) => {
+ let panelPopup = aDocument.getElementById("PanelUI-popup");
+ panelPopup.removeEventListener("popupshown", aCallback);
+ },
+ }],
+ ["backForward", {
+ query: "#back-button",
+ widgetName: "urlbar-container",
+ }],
+ ["bookmarks", {query: "#bookmarks-menu-button"}],
+ ["customize", {
+ query: (aDocument) => {
+ let customizeButton = aDocument.getElementById("PanelUI-customize");
+ return aDocument.getAnonymousElementByAttribute(customizeButton,
+ "class",
+ "toolbarbutton-icon");
+ },
+ widgetName: "PanelUI-customize",
+ }],
+ ["devtools", {query: "#developer-button"}],
+ ["help", {query: "#PanelUI-help"}],
+ ["home", {query: "#home-button"}],
+ ["forget", {
+ query: "#panic-button",
+ widgetName: "panic-button",
+ allowAdd: true,
+ }],
+ ["loop", {query: "#loop-button"}],
+ ["loop-newRoom", {
+ infoPanelPosition: "leftcenter topright",
+ query: (aDocument) => {
+ let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
+ if (!loopBrowser) {
+ return null;
+ }
+ // Use the parentElement full-width container of the button so our arrow
+ // doesn't overlap the panel contents much.
+ return loopBrowser.contentDocument.querySelector(".new-room-button").parentElement;
+ },
+ }],
+ ["loop-roomList", {
+ infoPanelPosition: "leftcenter topright",
+ query: (aDocument) => {
+ let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
+ if (!loopBrowser) {
+ return null;
+ }
+ return loopBrowser.contentDocument.querySelector(".room-list");
+ },
+ }],
+ ["loop-selectedRoomButtons", {
+ infoPanelOffsetY: -20,
+ infoPanelPosition: "start_after",
+ query: (aDocument) => {
+ let chatbox = aDocument.querySelector("chatbox[src^='about\:loopconversation'][selected]");
+
+ // Check that the real target actually exists
+ if (!chatbox || !chatbox.contentDocument ||
+ !chatbox.contentDocument.querySelector(".call-action-group")) {
+ return null;
+ }
+
+ // But anchor on the in the chatbox so the panel doesn't jump to undefined
+ // positions when the copy/email buttons disappear e.g. when the feedback form opens or
+ // somebody else joins the room.
+ return chatbox.content;
+ },
+ }],
+ ["loop-signInUpLink", {
+ query: (aDocument) => {
+ let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
+ if (!loopBrowser) {
+ return null;
+ }
+ return loopBrowser.contentDocument.querySelector(".signin-link");
+ },
+ }],
+ ["privateWindow", {query: "#privatebrowsing-button"}],
+ ["quit", {query: "#PanelUI-quit"}],
+ ["search", {
+ infoPanelOffsetX: 18,
+ infoPanelPosition: "after_start",
+ query: "#searchbar",
+ widgetName: "search-container",
+ }],
+ ["searchProvider", {
+ query: (aDocument) => {
+ let searchbar = aDocument.getElementById("searchbar");
+ if (searchbar.hasAttribute("oneoffui")) {
+ return null;
+ }
+ return aDocument.getAnonymousElementByAttribute(searchbar,
+ "anonid",
+ "searchbar-engine-button");
+ },
+ widgetName: "search-container",
+ }],
+ ["searchIcon", {
+ query: (aDocument) => {
+ let searchbar = aDocument.getElementById("searchbar");
+ if (!searchbar.hasAttribute("oneoffui")) {
+ return null;
+ }
+ return aDocument.getAnonymousElementByAttribute(searchbar,
+ "anonid",
+ "searchbar-search-button");
+ },
+ widgetName: "search-container",
+ }],
+ ["searchPrefsLink", {
+ query: (aDocument) => {
+ let element = null;
+ let searchbar = aDocument.getElementById("searchbar");
+ if (searchbar.hasAttribute("oneoffui")) {
+ let popup = aDocument.getElementById("PopupSearchAutoComplete");
+ if (popup.state != "open")
+ return null;
+ element = aDocument.getAnonymousElementByAttribute(popup,
+ "anonid",
+ "search-settings");
+ } else {
+ element = aDocument.getAnonymousElementByAttribute(searchbar,
+ "anonid",
+ "open-engine-manager");
+ }
+ if (!element || !UITour.isElementVisible(element)) {
+ return null;
+ }
+ return element;
+ },
+ }],
+ ["selectedTabIcon", {
+ query: (aDocument) => {
+ let selectedtab = aDocument.defaultView.gBrowser.selectedTab;
+ let element = aDocument.getAnonymousElementByAttribute(selectedtab,
+ "anonid",
+ "tab-icon-image");
+ if (!element || !UITour.isElementVisible(element)) {
+ return null;
+ }
+ return element;
+ },
+ }],
+ ["urlbar", {
+ query: "#urlbar",
+ widgetName: "urlbar-container",
+ }],
+ ["webide", {query: "#webide-button"}],
+ ]),
+
+ init: function() {
+ log.debug("Initializing UITour");
+ // Lazy getter is initialized here so it can be replicated any time
+ // in a test.
+ delete this.seenPageIDs;
+ Object.defineProperty(this, "seenPageIDs", {
+ get: this.restoreSeenPageIDs.bind(this),
+ configurable: true,
+ });
+
+ delete this.url;
+ XPCOMUtils.defineLazyGetter(this, "url", function () {
+ return Services.urlFormatter.formatURLPref("browser.uitour.url");
+ });
+
+ // Clear the availableTargetsCache on widget changes.
+ let listenerMethods = [
+ "onWidgetAdded",
+ "onWidgetMoved",
+ "onWidgetRemoved",
+ "onWidgetReset",
+ "onAreaReset",
+ ];
+ CustomizableUI.addListener(listenerMethods.reduce((listener, method) => {
+ listener[method] = () => this.availableTargetsCache.clear();
+ return listener;
+ }, {}));
+ },
+
+ restoreSeenPageIDs: function() {
+ delete this.seenPageIDs;
+
+ if (UITelemetry.enabled) {
+ let dateThreshold = Date.now() - SEENPAGEID_EXPIRY;
+
+ try {
+ let data = Services.prefs.getCharPref(PREF_SEENPAGEIDS);
+ data = new Map(JSON.parse(data));
+
+ for (let [pageID, details] of data) {
+
+ if (typeof pageID != "string" ||
+ typeof details != "object" ||
+ typeof details.lastSeen != "number" ||
+ details.lastSeen < dateThreshold) {
+
+ data.delete(pageID);
+ }
+ }
+
+ this.seenPageIDs = data;
+ } catch (e) {}
+ }
+
+ if (!this.seenPageIDs)
+ this.seenPageIDs = new Map();
+
+ this.persistSeenIDs();
+
+ return this.seenPageIDs;
+ },
+
+ addSeenPageID: function(aPageID) {
+ if (!UITelemetry.enabled)
+ return;
+
+ this.seenPageIDs.set(aPageID, {
+ lastSeen: Date.now(),
+ });
+
+ this.persistSeenIDs();
+ },
+
+ persistSeenIDs: function() {
+ if (this.seenPageIDs.size === 0) {
+ Services.prefs.clearUserPref(PREF_SEENPAGEIDS);
+ return;
+ }
+
+ Services.prefs.setCharPref(PREF_SEENPAGEIDS,
+ JSON.stringify([...this.seenPageIDs]));
+ },
+
+ onPageEvent: function(aMessage, aEvent) {
+ let contentDocument = null;
+ let browser = aMessage.target;
+ let window = browser.ownerDocument.defaultView;
+ let tab = window.gBrowser.getTabForBrowser(browser);
+ let messageManager = browser.messageManager;
+
+ log.debug("onPageEvent:", aEvent.detail);
+
+ if (typeof aEvent.detail != "object") {
+ log.warn("Malformed event - detail not an object");
+ return false;
+ }
+
+ let action = aEvent.detail.action;
+ if (typeof action != "string" || !action) {
+ log.warn("Action not defined");
+ return false;
+ }
+
+ let data = aEvent.detail.data;
+ if (typeof data != "object") {
+ log.warn("Malformed event - data not an object");
+ return false;
+ }
+
+ // Do this before bailing if there's no tab, so later we can pick up the pieces:
+ window.gBrowser.tabContainer.addEventListener("TabSelect", this);
+
+ if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
+ contentDocument = browser.contentWindow.document;
+ if (!tab) {
+ // This should only happen while detaching a tab:
+ if (this._detachingTab) {
+ log.debug("Got event while detatching a tab");
+ this._queuedEvents.push(aEvent);
+ this._pendingDoc = Cu.getWeakReference(contentDocument);
+ return;
+ }
+ log.error("Discarding tabless UITour event (" + action + ") while not detaching a tab." +
+ "This shouldn't happen!");
+ return;
+ }
+ }
+
+ switch (action) {
+ case "registerPageID": {
+ // This is only relevant if Telemtry is enabled.
+ if (!UITelemetry.enabled) {
+ log.debug("registerPageID: Telemery disabled, not doing anything");
+ break;
+ }
+
+ // We don't want to allow BrowserUITelemetry.BUCKET_SEPARATOR in the
+ // pageID, as it could make parsing the telemetry bucket name difficult.
+ if (typeof data.pageID != "string" ||
+ data.pageID.contains(BrowserUITelemetry.BUCKET_SEPARATOR)) {
+ log.warn("registerPageID: Invalid page ID specified");
+ break;
+ }
+
+ this.addSeenPageID(data.pageID);
+
+ // Store tabs and windows separately so we don't need to loop over all
+ // tabs when a window is closed.
+ this.pageIDSourceTabs.set(tab, data.pageID);
+ this.pageIDSourceWindows.set(window, data.pageID);
+
+ this.setTelemetryBucket(data.pageID);
+
+ break;
+ }
+
+ case "showHighlight": {
+ let targetPromise = this.getTarget(window, data.target);
+ targetPromise.then(target => {
+ if (!target.node) {
+ log.error("UITour: Target could not be resolved: " + data.target);
+ return;
+ }
+ let effect = undefined;
+ if (this.highlightEffects.indexOf(data.effect) !== -1) {
+ effect = data.effect;
+ }
+ this.showHighlight(window, target, effect);
+ }).catch(log.error);
+ break;
+ }
+
+ case "hideHighlight": {
+ this.hideHighlight(window);
+ break;
+ }
+
+ case "showInfo": {
+ let targetPromise = this.getTarget(window, data.target, true);
+ targetPromise.then(target => {
+ if (!target.node) {
+ log.error("UITour: Target could not be resolved: " + data.target);
+ return;
+ }
+
+ let iconURL = null;
+ if (typeof data.icon == "string")
+ iconURL = this.resolveURL(browser, data.icon);
+
+ let buttons = [];
+ if (Array.isArray(data.buttons) && data.buttons.length > 0) {
+ for (let buttonData of data.buttons) {
+ if (typeof buttonData == "object" &&
+ typeof buttonData.label == "string" &&
+ typeof buttonData.callbackID == "string") {
+ let button = {
+ label: buttonData.label,
+ callbackID: buttonData.callbackID,
+ };
+
+ if (typeof buttonData.icon == "string")
+ button.iconURL = this.resolveURL(browser, buttonData.icon);
+
+ if (typeof buttonData.style == "string")
+ button.style = buttonData.style;
+
+ buttons.push(button);
+
+ if (buttons.length == MAX_BUTTONS) {
+ log.warn("showInfo: Reached limit of allowed number of buttons");
+ break;
+ }
+ }
+ }
+ }
+
+ let infoOptions = {};
+
+ if (typeof data.closeButtonCallbackID == "string")
+ infoOptions.closeButtonCallbackID = data.closeButtonCallbackID;
+ if (typeof data.targetCallbackID == "string")
+ infoOptions.targetCallbackID = data.targetCallbackID;
+
+ this.showInfo(window, messageManager, target, data.title, data.text, iconURL, buttons, infoOptions);
+ }).catch(log.error);
+ break;
+ }
+
+ case "hideInfo": {
+ this.hideInfo(window);
+ break;
+ }
+
+ case "previewTheme": {
+ this.previewTheme(data.theme);
+ break;
+ }
+
+ case "resetTheme": {
+ this.resetTheme();
+ break;
+ }
+
+ case "addPinnedTab": {
+ this.ensurePinnedTab(window, true);
+ break;
+ }
+
+ case "removePinnedTab": {
+ this.removePinnedTab(window);
+ break;
+ }
+
+ case "showMenu": {
+ this.showMenu(window, data.name, () => {
+ if (typeof data.showCallbackID == "string")
+ this.sendPageCallback(messageManager, data.showCallbackID);
+ });
+ break;
+ }
+
+ case "hideMenu": {
+ this.hideMenu(window, data.name);
+ break;
+ }
+
+ case "startUrlbarCapture": {
+ if (typeof data.text != "string" || !data.text ||
+ typeof data.url != "string" || !data.url) {
+ log.warn("startUrlbarCapture: Text or URL not specified");
+ return false;
+ }
+
+ let uri = null;
+ try {
+ uri = Services.io.newURI(data.url, null, null);
+ } catch (e) {
+ log.warn("startUrlbarCapture: Malformed URL specified");
+ return false;
+ }
+
+ let secman = Services.scriptSecurityManager;
+ let principal = contentDocument.nodePrincipal;
+ let flags = secman.DISALLOW_INHERIT_PRINCIPAL;
+ try {
+ secman.checkLoadURIWithPrincipal(principal, uri, flags);
+ } catch (e) {
+ log.warn("startUrlbarCapture: Orginating page doesn't have permission to open specified URL");
+ return false;
+ }
+
+ this.startUrlbarCapture(window, data.text, data.url);
+ break;
+ }
+
+ case "endUrlbarCapture": {
+ this.endUrlbarCapture(window);
+ break;
+ }
+
+ case "getConfiguration": {
+ if (typeof data.configuration != "string") {
+ log.warn("getConfiguration: No configuration option specified");
+ return false;
+ }
+
+ this.getConfiguration(messageManager, window, data.configuration, data.callbackID);
+ break;
+ }
+
+ case "setConfiguration": {
+ if (typeof data.configuration != "string") {
+ log.warn("setConfiguration: No configuration option specified");
+ return false;
+ }
+
+ this.setConfiguration(data.configuration, data.value);
+ break;
+ }
+
+ case "showFirefoxAccounts": {
+ // 'signup' is the only action that makes sense currently, so we don't
+ // accept arbitrary actions just to be safe...
+ // We want to replace the current tab.
+ contentDocument.location.href = "about:accounts?action=signup&entrypoint=uitour";
+ break;
+ }
+
+ case "resetFirefox": {
+ // Open a reset profile dialog window.
+ ResetProfile.openConfirmationDialog(window);
+ break;
+ }
+
+ case "addNavBarWidget": {
+ // Add a widget to the toolbar
+ let targetPromise = this.getTarget(window, data.name);
+ targetPromise.then(target => {
+ this.addNavBarWidget(target, messageManager, data.callbackID);
+ }).catch(log.error);
+ break;
+ }
+
+ case "setDefaultSearchEngine": {
+ let enginePromise = this.selectSearchEngine(data.identifier);
+ enginePromise.catch(Cu.reportError);
+ break;
+ }
+
+ case "setTreatmentTag": {
+ let name = data.name;
+ let value = data.value;
+ let string = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+ string.data = value;
+ Services.prefs.setComplexValue("browser.uitour.treatment." + name,
+ Ci.nsISupportsString, string);
+ UITourHealthReport.recordTreatmentTag(name, value);
+ break;
+ }
+
+ case "getTreatmentTag": {
+ let name = data.name;
+ let value;
+ try {
+ value = Services.prefs.getComplexValue("browser.uitour.treatment." + name,
+ Ci.nsISupportsString).data;
+ } catch (ex) {}
+ this.sendPageCallback(messageManager, data.callbackID, { value: value });
+ break;
+ }
+
+ case "setSearchTerm": {
+ let targetPromise = this.getTarget(window, "search");
+ targetPromise.then(target => {
+ let searchbar = target.node;
+ searchbar.value = data.term;
+ searchbar.inputChanged();
+ }).then(null, Cu.reportError);
+ break;
+ }
+
+ case "openSearchPanel": {
+ let targetPromise = this.getTarget(window, "search");
+ targetPromise.then(target => {
+ let searchbar = target.node;
+
+ if (searchbar.textbox.open) {
+ this.sendPageCallback(messageManager, data.callbackID);
+ } else {
+ let onPopupShown = () => {
+ searchbar.textbox.popup.removeEventListener("popupshown", onPopupShown);
+ this.sendPageCallback(messageManager, data.callbackID);
+ };
+
+ searchbar.textbox.popup.addEventListener("popupshown", onPopupShown);
+ searchbar.openSuggestionsPanel();
+ }
+ }).then(null, Cu.reportError);
+ break;
+ }
+
+ case "ping": {
+ if (typeof data.callbackID == "string")
+ this.sendPageCallback(messageManager, data.callbackID);
+ break;
+ }
+ }
+
+ if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
+ if (!this.originTabs.has(window)) {
+ this.originTabs.set(window, new Set());
+ }
+
+ this.originTabs.get(window).add(tab);
+ tab.addEventListener("TabClose", this);
+ tab.addEventListener("TabBecomingWindow", this);
+ window.addEventListener("SSWindowClosing", this);
+ }
+
+ return true;
+ },
+
+ handleEvent: function(aEvent) {
+ switch (aEvent.type) {
+ case "pagehide": {
+ let window = this.getChromeWindow(aEvent.target);
+ this.teardownTour(window);
+ break;
+ }
+
+ case "TabBecomingWindow":
+ this._detachingTab = true;
+ // Fall through
+ case "TabClose": {
+ let tab = aEvent.target;
+ if (this.pageIDSourceTabs.has(tab)) {
+ let pageID = this.pageIDSourceTabs.get(tab);
+
+ // Delete this from the window cache, so if the window is closed we
+ // don't expire this page ID twice.
+ let window = tab.ownerDocument.defaultView;
+ if (this.pageIDSourceWindows.get(window) == pageID)
+ this.pageIDSourceWindows.delete(window);
+
+ this.setExpiringTelemetryBucket(pageID, "closed");
+ }
+
+ let window = tab.ownerDocument.defaultView;
+ this.teardownTour(window);
+ break;
+ }
+
+ case "TabSelect": {
+ if (aEvent.detail && aEvent.detail.previousTab) {
+ let previousTab = aEvent.detail.previousTab;
+
+ if (this.pageIDSourceTabs.has(previousTab)) {
+ let pageID = this.pageIDSourceTabs.get(previousTab);
+ this.setExpiringTelemetryBucket(pageID, "inactive");
+ }
+ }
+
+ let window = aEvent.target.ownerDocument.defaultView;
+ let selectedTab = window.gBrowser.selectedTab;
+ let pinnedTab = this.pinnedTabs.get(window);
+ if (pinnedTab && pinnedTab.tab == selectedTab)
+ break;
+ let originTabs = this.originTabs.get(window);
+ if (originTabs && originTabs.has(selectedTab))
+ break;
+
+ let pendingDoc;
+ if (this._detachingTab && this._pendingDoc && (pendingDoc = this._pendingDoc.get())) {
+ if (selectedTab.linkedBrowser.contentDocument == pendingDoc) {
+ if (!this.originTabs.get(window)) {
+ this.originTabs.set(window, new Set());
+ }
+ this.originTabs.get(window).add(selectedTab);
+ this.pendingDoc = null;
+ this._detachingTab = false;
+ while (this._queuedEvents.length) {
+ try {
+ this.onPageEvent(this._queuedEvents.shift());
+ } catch (ex) {
+ log.error(ex);
+ }
+ }
+ break;
+ }
+ }
+
+ this.teardownTour(window);
+ break;
+ }
+
+ case "SSWindowClosing": {
+ let window = aEvent.target;
+ if (this.pageIDSourceWindows.has(window)) {
+ let pageID = this.pageIDSourceWindows.get(window);
+ this.setExpiringTelemetryBucket(pageID, "closed");
+ }
+
+ this.teardownTour(window, true);
+ break;
+ }
+
+ case "input": {
+ if (aEvent.target.id == "urlbar") {
+ let window = aEvent.target.ownerDocument.defaultView;
+ this.handleUrlbarInput(window);
+ }
+ break;
+ }
+ }
+ },
+
+ setTelemetryBucket: function(aPageID) {
+ let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID;
+ BrowserUITelemetry.setBucket(bucket);
+ },
+
+ setExpiringTelemetryBucket: function(aPageID, aType) {
+ let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID +
+ BrowserUITelemetry.BUCKET_SEPARATOR + aType;
+
+ BrowserUITelemetry.setExpiringBucket(bucket,
+ BUCKET_TIMESTEPS);
+ },
+
+ // This is registered with UITelemetry by BrowserUITelemetry, so that UITour
+ // can remain lazy-loaded on-demand.
+ getTelemetry: function() {
+ return {
+ seenPageIDs: [...this.seenPageIDs.keys()],
+ };
+ },
+
+ teardownTour: function(aWindow, aWindowClosing = false) {
+ log.debug("teardownTour: aWindowClosing = " + aWindowClosing);
+ aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
+ aWindow.removeEventListener("SSWindowClosing", this);
+
+ let originTabs = this.originTabs.get(aWindow);
+ if (originTabs) {
+ for (let tab of originTabs) {
+ tab.removeEventListener("TabClose", this);
+ tab.removeEventListener("TabBecomingWindow", this);
+ }
+ }
+ this.originTabs.delete(aWindow);
+
+ if (!aWindowClosing) {
+ this.hideHighlight(aWindow);
+ this.hideInfo(aWindow);
+ // Ensure the menu panel is hidden before calling recreatePopup so popup events occur.
+ this.hideMenu(aWindow, "appMenu");
+ this.hideMenu(aWindow, "loop");
+ }
+
+ // Clean up panel listeners after we may have called hideMenu above.
+ aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hideAppMenuAnnotations);
+ aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hideAppMenuAnnotations);
+ aWindow.PanelUI.panel.removeEventListener("popuphidden", this.onPanelHidden);
+ let loopPanel = aWindow.document.getElementById("loop-notification-panel");
+ loopPanel.removeEventListener("popuphidden", this.onPanelHidden);
+ loopPanel.removeEventListener("popuphiding", this.hideLoopPanelAnnotations);
+
+ this.endUrlbarCapture(aWindow);
+ this.removePinnedTab(aWindow);
+ this.resetTheme();
+ },
+
+ getChromeWindow: function(aContentDocument) {
+ return aContentDocument.defaultView
+ .window
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow)
+ .wrappedJSObject;
+ },
+
+ // This function is copied to UITourListener.
+ isSafeScheme: function(aURI) {
+ let allowedSchemes = new Set(["https", "about"]);
+ if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
+ allowedSchemes.add("http");
+
+ if (!allowedSchemes.has(aURI.scheme)) {
+ log.error("Unsafe scheme:", aURI.scheme);
+ return false;
+ }
+
+ return true;
+ },
+
+ resolveURL: function(aBrowser, aURL) {
+ try {
+ let uri = Services.io.newURI(aURL, null, aBrowser.currentURI);
+
+ if (!this.isSafeScheme(uri))
+ return null;
+
+ return uri.spec;
+ } catch (e) {}
+
+ return null;
+ },
+
+ sendPageCallback: function(aMessageManager, aCallbackID, aData = {}) {
+ let detail = {data: aData, callbackID: aCallbackID};
+ log.debug("sendPageCallback", detail);
+ aMessageManager.sendAsyncMessage("UITour:SendPageCallback", detail);
+ },
+
+ isElementVisible: function(aElement) {
+ let targetStyle = aElement.ownerDocument.defaultView.getComputedStyle(aElement);
+ return !aElement.ownerDocument.hidden &&
+ targetStyle.display != "none" &&
+ targetStyle.visibility == "visible";
+ },
+
+ getTarget: function(aWindow, aTargetName, aSticky = false) {
+ log.debug("getTarget:", aTargetName);
+ let deferred = Promise.defer();
+ if (typeof aTargetName != "string" || !aTargetName) {
+ log.warn("getTarget: Invalid target name specified");
+ deferred.reject("Invalid target name specified");
+ return deferred.promise;
+ }
+
+ if (aTargetName == "pinnedTab") {
+ deferred.resolve({
+ targetName: aTargetName,
+ node: this.ensurePinnedTab(aWindow, aSticky)
+ });
+ return deferred.promise;
+ }
+
+ if (aTargetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
+ let engineID = aTargetName.slice(TARGET_SEARCHENGINE_PREFIX.length);
+ return this.getSearchEngineTarget(aWindow, engineID);
+ }
+
+ let targetObject = this.targets.get(aTargetName);
+ if (!targetObject) {
+ log.warn("getTarget: The specified target name is not in the allowed set");
+ deferred.reject("The specified target name is not in the allowed set");
+ return deferred.promise;
+ }
+
+ let targetQuery = targetObject.query;
+ aWindow.PanelUI.ensureReady().then(() => {
+ let node;
+ if (typeof targetQuery == "function") {
+ try {
+ node = targetQuery(aWindow.document);
+ } catch (ex) {
+ log.warn("getTarget: Error running target query:", ex);
+ node = null;
+ }
+ } else {
+ node = aWindow.document.querySelector(targetQuery);
+ }
+
+ deferred.resolve({
+ addTargetListener: targetObject.addTargetListener,
+ infoPanelOffsetX: targetObject.infoPanelOffsetX,
+ infoPanelOffsetY: targetObject.infoPanelOffsetY,
+ infoPanelPosition: targetObject.infoPanelPosition,
+ node: node,
+ removeTargetListener: targetObject.removeTargetListener,
+ targetName: aTargetName,
+ widgetName: targetObject.widgetName,
+ allowAdd: targetObject.allowAdd,
+ });
+ }).catch(log.error);
+ return deferred.promise;
+ },
+
+ targetIsInAppMenu: function(aTarget) {
+ let placement = CustomizableUI.getPlacementOfWidget(aTarget.widgetName || aTarget.node.id);
+ if (placement && placement.area == CustomizableUI.AREA_PANEL) {
+ return true;
+ }
+
+ let targetElement = aTarget.node;
+ // Use the widget for filtering if it exists since the target may be the icon inside.
+ if (aTarget.widgetName) {
+ targetElement = aTarget.node.ownerDocument.getElementById(aTarget.widgetName);
+ }
+
+ // Handle the non-customizable buttons at the bottom of the menu which aren't proper widgets.
+ return targetElement.id.startsWith("PanelUI-")
+ && targetElement.id != "PanelUI-button";
+ },
+
+ /**
+ * Called before opening or after closing a highlight or info panel to see if
+ * we need to open or close the appMenu to see the annotation's anchor.
+ */
+ _setAppMenuStateForAnnotation: function(aWindow, aAnnotationType, aShouldOpenForHighlight, aCallback = null) {
+ log.debug("_setAppMenuStateForAnnotation:", aAnnotationType);
+ log.debug("_setAppMenuStateForAnnotation: Menu is expected to be:", aShouldOpenForHighlight ? "open" : "closed");
+
+ // If the panel is in the desired state, we're done.
+ let panelIsOpen = aWindow.PanelUI.panel.state != "closed";
+ if (aShouldOpenForHighlight == panelIsOpen) {
+ log.debug("_setAppMenuStateForAnnotation: Panel already in expected state");
+ if (aCallback)
+ aCallback();
+ return;
+ }
+
+ // Don't close the menu if it wasn't opened by us (e.g. via showmenu instead).
+ if (!aShouldOpenForHighlight && !this.appMenuOpenForAnnotation.has(aAnnotationType)) {
+ log.debug("_setAppMenuStateForAnnotation: Menu not opened by us, not closing");
+ if (aCallback)
+ aCallback();
+ return;
+ }
+
+ if (aShouldOpenForHighlight) {
+ this.appMenuOpenForAnnotation.add(aAnnotationType);
+ } else {
+ this.appMenuOpenForAnnotation.delete(aAnnotationType);
+ }
+
+ // Actually show or hide the menu
+ if (this.appMenuOpenForAnnotation.size) {
+ log.debug("_setAppMenuStateForAnnotation: Opening the menu");
+ this.showMenu(aWindow, "appMenu", aCallback);
+ } else {
+ log.debug("_setAppMenuStateForAnnotation: Closing the menu");
+ this.hideMenu(aWindow, "appMenu");
+ if (aCallback)
+ aCallback();
+ }
+
+ },
+
+ previewTheme: function(aTheme) {
+ let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
+ let data = LightweightThemeManager.parseTheme(aTheme, origin);
+ if (data)
+ LightweightThemeManager.previewTheme(data);
+ },
+
+ resetTheme: function() {
+ LightweightThemeManager.resetPreview();
+ },
+
+ ensurePinnedTab: function(aWindow, aSticky = false) {
+ let tabInfo = this.pinnedTabs.get(aWindow);
+
+ if (tabInfo) {
+ tabInfo.sticky = tabInfo.sticky || aSticky;
+ } else {
+ let url = Services.urlFormatter.formatURLPref("browser.uitour.pinnedTabUrl");
+
+ let tab = aWindow.gBrowser.addTab(url);
+ aWindow.gBrowser.pinTab(tab);
+ tab.addEventListener("TabClose", () => {
+ this.pinnedTabs.delete(aWindow);
+ });
+
+ tabInfo = {
+ tab: tab,
+ sticky: aSticky
+ };
+ this.pinnedTabs.set(aWindow, tabInfo);
+ }
+
+ return tabInfo.tab;
+ },
+
+ removePinnedTab: function(aWindow) {
+ let tabInfo = this.pinnedTabs.get(aWindow);
+ if (tabInfo)
+ aWindow.gBrowser.removeTab(tabInfo.tab);
+ },
+
+ /**
+ * @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
+ * are in a sub-frame so the defaultView is not the same as the chrome
+ * window.
+ * @param aTarget The element to highlight.
+ * @param aEffect (optional) The effect to use from UITour.highlightEffects or "none".
+ * @see UITour.highlightEffects
+ */
+ showHighlight: function(aChromeWindow, aTarget, aEffect = "none") {
+ function showHighlightPanel() {
+ if (aTarget.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
+ // This won't affect normal higlights done via the panel, so we need to
+ // manually hide those.
+ this.hideHighlight(aChromeWindow);
+ aTarget.node.setAttribute("_moz-menuactive", true);
+ return;
+ }
+
+ // Conversely, highlights for search engines are highlighted via CSS
+ // rather than a panel, so need to be manually removed.
+ this._hideSearchEngineHighlight(aChromeWindow);
+
+ let highlighter = aChromeWindow.document.getElementById("UITourHighlight");
+
+ let effect = aEffect;
+ if (effect == "random") {
+ // Exclude "random" from the randomly selected effects.
+ let randomEffect = 1 + Math.floor(Math.random() * (this.highlightEffects.length - 1));
+ if (randomEffect == this.highlightEffects.length)
+ randomEffect--; // On the order of 1 in 2^62 chance of this happening.
+ effect = this.highlightEffects[randomEffect];
+ }
+ // Toggle the effect attribute to "none" and flush layout before setting it so the effect plays.
+ highlighter.setAttribute("active", "none");
+ aChromeWindow.getComputedStyle(highlighter).animationName;
+ highlighter.setAttribute("active", effect);
+ highlighter.parentElement.setAttribute("targetName", aTarget.targetName);
+ highlighter.parentElement.hidden = false;
+
+ let highlightAnchor;
+ // If the target is in the overflow panel, just highlight the overflow button.
+ if (aTarget.node.getAttribute("overflowedItem")) {
+ let doc = aTarget.node.ownerDocument;
+ let placement = CustomizableUI.getPlacementOfWidget(aTarget.widgetName || aTarget.node.id);
+ let areaNode = doc.getElementById(placement.area);
+ highlightAnchor = areaNode.overflowable._chevron;
+ } else {
+ highlightAnchor = aTarget.node;
+ }
+ let targetRect = highlightAnchor.getBoundingClientRect();
+ let highlightHeight = targetRect.height;
+ let highlightWidth = targetRect.width;
+ let minDimension = Math.min(highlightHeight, highlightWidth);
+ let maxDimension = Math.max(highlightHeight, highlightWidth);
+
+ // If the dimensions are within 200% of each other (to include the bookmarks button),
+ // make the highlight a circle with the largest dimension as the diameter.
+ if (maxDimension / minDimension <= 3.0) {
+ highlightHeight = highlightWidth = maxDimension;
+ highlighter.style.borderRadius = "100%";
+ } else {
+ highlighter.style.borderRadius = "";
+ }
+
+ highlighter.style.height = highlightHeight + "px";
+ highlighter.style.width = highlightWidth + "px";
+
+ // Close a previous highlight so we can relocate the panel.
+ if (highlighter.parentElement.state == "showing" || highlighter.parentElement.state == "open") {
+ log.debug("showHighlight: Closing previous highlight first");
+ highlighter.parentElement.hidePopup();
+ }
+ /* The "overlap" position anchors from the top-left but we want to centre highlights at their
+ minimum size. */
+ let highlightWindow = aChromeWindow;
+ let containerStyle = highlightWindow.getComputedStyle(highlighter.parentElement);
+ let paddingTopPx = 0 - parseFloat(containerStyle.paddingTop);
+ let paddingLeftPx = 0 - parseFloat(containerStyle.paddingLeft);
+ let highlightStyle = highlightWindow.getComputedStyle(highlighter);
+ let highlightHeightWithMin = Math.max(highlightHeight, parseFloat(highlightStyle.minHeight));
+ let highlightWidthWithMin = Math.max(highlightWidth, parseFloat(highlightStyle.minWidth));
+ let offsetX = paddingTopPx
+ - (Math.max(0, highlightWidthWithMin - targetRect.width) / 2);
+ let offsetY = paddingLeftPx
+ - (Math.max(0, highlightHeightWithMin - targetRect.height) / 2);
+ this._addAnnotationPanelMutationObserver(highlighter.parentElement);
+ highlighter.parentElement.openPopup(highlightAnchor, "overlap", offsetX, offsetY);
+ }
+
+ // Prevent showing a panel at an undefined position.
+ if (!this.isElementVisible(aTarget.node)) {
+ log.warn("showHighlight: Not showing a highlight since the target isn't visible", aTarget);
+ return;
+ }
+
+ this._setAppMenuStateForAnnotation(aChromeWindow, "highlight",
+ this.targetIsInAppMenu(aTarget),
+ showHighlightPanel.bind(this));
+ },
+
+ hideHighlight: function(aWindow) {
+ let tabData = this.pinnedTabs.get(aWindow);
+ if (tabData && !tabData.sticky)
+ this.removePinnedTab(aWindow);
+
+ let highlighter = aWindow.document.getElementById("UITourHighlight");
+ this._removeAnnotationPanelMutationObserver(highlighter.parentElement);
+ highlighter.parentElement.hidePopup();
+ highlighter.removeAttribute("active");
+
+ this._setAppMenuStateForAnnotation(aWindow, "highlight", false);
+ this._hideSearchEngineHighlight(aWindow);
+ },
+
+ _hideSearchEngineHighlight: function(aWindow) {
+ // We special case highlighting items in the search engines dropdown,
+ // so just blindly remove any highlight there.
+ let searchMenuBtn = null;
+ try {
+ searchMenuBtn = this.targets.get("searchProvider").query(aWindow.document);
+ } catch (e) { /* This is ok to fail. */ }
+ if (searchMenuBtn) {
+ let searchPopup = aWindow.document
+ .getAnonymousElementByAttribute(searchMenuBtn,
+ "anonid",
+ "searchbar-popup");
+ for (let menuItem of searchPopup.children)
+ menuItem.removeAttribute("_moz-menuactive");
+ }
+ },
+
+ /**
+ * Show an info panel.
+ *
+ * @param {ChromeWindow} aChromeWindow
+ * @param {nsIMessageSender} aMessageManager
+ * @param {Node} aAnchor
+ * @param {String} [aTitle=""]
+ * @param {String} [aDescription=""]
+ * @param {String} [aIconURL=""]
+ * @param {Object[]} [aButtons=[]]
+ * @param {Object} [aOptions={}]
+ * @param {String} [aOptions.closeButtonCallbackID]
+ */
+ showInfo: function(aChromeWindow, aMessageManager, aAnchor, aTitle = "", aDescription = "", aIconURL = "",
+ aButtons = [], aOptions = {}) {
+ function showInfoPanel(aAnchorEl) {
+ aAnchorEl.focus();
+
+ let document = aChromeWindow.document;
+ let tooltip = document.getElementById("UITourTooltip");
+ let tooltipTitle = document.getElementById("UITourTooltipTitle");
+ let tooltipDesc = document.getElementById("UITourTooltipDescription");
+ let tooltipIcon = document.getElementById("UITourTooltipIcon");
+ let tooltipIconContainer = document.getElementById("UITourTooltipIconContainer");
+ let tooltipButtons = document.getElementById("UITourTooltipButtons");
+
+ if (tooltip.state == "showing" || tooltip.state == "open") {
+ tooltip.hidePopup();
+ }
+
+ tooltipTitle.textContent = aTitle || "";
+ tooltipDesc.textContent = aDescription || "";
+ tooltipIcon.src = aIconURL || "";
+ tooltipIconContainer.hidden = !aIconURL;
+
+ while (tooltipButtons.firstChild)
+ tooltipButtons.firstChild.remove();
+
+ for (let button of aButtons) {
+ let el = document.createElement("button");
+ el.setAttribute("label", button.label);
+ if (button.iconURL)
+ el.setAttribute("image", button.iconURL);
+
+ if (button.style == "link")
+ el.setAttribute("class", "button-link");
+
+ if (button.style == "primary")
+ el.setAttribute("class", "button-primary");
+
+ let callbackID = button.callbackID;
+ el.addEventListener("command", event => {
+ tooltip.hidePopup();
+ this.sendPageCallback(aMessageManager, callbackID);
+ });
+
+ tooltipButtons.appendChild(el);
+ }
+
+ tooltipButtons.hidden = !aButtons.length;
+
+ let tooltipClose = document.getElementById("UITourTooltipClose");
+ let closeButtonCallback = (event) => {
+ this.hideInfo(document.defaultView);
+ if (aOptions && aOptions.closeButtonCallbackID)
+ this.sendPageCallback(aMessageManager, aOptions.closeButtonCallbackID);
+ };
+ tooltipClose.addEventListener("command", closeButtonCallback);
+
+ let targetCallback = (event) => {
+ let details = {
+ target: aAnchor.targetName,
+ type: event.type,
+ };
+ this.sendPageCallback(aMessageManager, aOptions.targetCallbackID, details);
+ };
+ if (aOptions.targetCallbackID && aAnchor.addTargetListener) {
+ aAnchor.addTargetListener(document, targetCallback);
+ }
+
+ tooltip.addEventListener("popuphiding", function tooltipHiding(event) {
+ tooltip.removeEventListener("popuphiding", tooltipHiding);
+ tooltipClose.removeEventListener("command", closeButtonCallback);
+ if (aOptions.targetCallbackID && aAnchor.removeTargetListener) {
+ aAnchor.removeTargetListener(document, targetCallback);
+ }
+ });
+
+ tooltip.setAttribute("targetName", aAnchor.targetName);
+ tooltip.hidden = false;
+ let alignment = "bottomcenter topright";
+ if (aAnchor.infoPanelPosition) {
+ alignment = aAnchor.infoPanelPosition;
+ }
+
+ let { infoPanelOffsetX: xOffset, infoPanelOffsetY: yOffset } = aAnchor;
+
+ this._addAnnotationPanelMutationObserver(tooltip);
+ tooltip.openPopup(aAnchorEl, alignment, xOffset || 0, yOffset || 0);
+ if (tooltip.state == "closed") {
+ document.defaultView.addEventListener("endmodalstate", function endModalStateHandler() {
+ document.defaultView.removeEventListener("endmodalstate", endModalStateHandler);
+ tooltip.openPopup(aAnchorEl, alignment);
+ }, false);
+ }
+ }
+
+ // Prevent showing a panel at an undefined position.
+ if (!this.isElementVisible(aAnchor.node)) {
+ log.warn("showInfo: Not showing since the target isn't visible", aAnchor);
+ return;
+ }
+
+ // Due to a platform limitation, we can't anchor a panel to an element in a
+ // . So we can't support showing info panels for search engines.
+ if (aAnchor.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX))
+ return;
+
+ this._setAppMenuStateForAnnotation(aChromeWindow, "info",
+ this.targetIsInAppMenu(aAnchor),
+ showInfoPanel.bind(this, aAnchor.node));
+ },
+
+ hideInfo: function(aWindow) {
+ let document = aWindow.document;
+
+ let tooltip = document.getElementById("UITourTooltip");
+ this._removeAnnotationPanelMutationObserver(tooltip);
+ tooltip.hidePopup();
+ this._setAppMenuStateForAnnotation(aWindow, "info", false);
+
+ let tooltipButtons = document.getElementById("UITourTooltipButtons");
+ while (tooltipButtons.firstChild)
+ tooltipButtons.firstChild.remove();
+ },
+
+ showMenu: function(aWindow, aMenuName, aOpenCallback = null) {
+ function openMenuButton(aMenuBtn) {
+ if (!aMenuBtn || !aMenuBtn.boxObject || aMenuBtn.open) {
+ if (aOpenCallback)
+ aOpenCallback();
+ return;
+ }
+ if (aOpenCallback)
+ aMenuBtn.addEventListener("popupshown", onPopupShown);
+ aMenuBtn.boxObject.openMenu(true);
+ }
+ function onPopupShown(event) {
+ this.removeEventListener("popupshown", onPopupShown);
+ aOpenCallback(event);
+ }
+
+ if (aMenuName == "appMenu") {
+ aWindow.PanelUI.panel.setAttribute("noautohide", "true");
+ // If the popup is already opened, don't recreate the widget as it may cause a flicker.
+ if (aWindow.PanelUI.panel.state != "open") {
+ this.recreatePopup(aWindow.PanelUI.panel);
+ }
+ aWindow.PanelUI.panel.addEventListener("popuphiding", this.hideAppMenuAnnotations);
+ aWindow.PanelUI.panel.addEventListener("ViewShowing", this.hideAppMenuAnnotations);
+ aWindow.PanelUI.panel.addEventListener("popuphidden", this.onPanelHidden);
+ if (aOpenCallback) {
+ aWindow.PanelUI.panel.addEventListener("popupshown", onPopupShown);
+ }
+ aWindow.PanelUI.show();
+ } else if (aMenuName == "bookmarks") {
+ let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
+ openMenuButton(menuBtn);
+ } else if (aMenuName == "loop") {
+ let toolbarButton = aWindow.LoopUI.toolbarButton;
+ // It's possible to have a node that isn't placed anywhere
+ if (!toolbarButton || !toolbarButton.node ||
+ !CustomizableUI.getPlacementOfWidget(toolbarButton.node.id)) {
+ log.debug("Can't show the Loop menu since the toolbarButton isn't placed");
+ return;
+ }
+
+ let panel = aWindow.document.getElementById("loop-notification-panel");
+ panel.setAttribute("noautohide", true);
+ if (panel.state != "open") {
+ this.recreatePopup(panel);
+ this.availableTargetsCache.clear();
+ }
+
+ // An event object is expected but we don't want to toggle the panel with a click if the panel
+ // is already open.
+ aWindow.LoopUI.openCallPanel({ target: toolbarButton.node, }, "rooms").then(() => {
+ if (aOpenCallback) {
+ aOpenCallback();
+ }
+ });
+ panel.addEventListener("popuphidden", this.onPanelHidden);
+ panel.addEventListener("popuphiding", this.hideLoopPanelAnnotations);
+ } else if (aMenuName == "searchEngines") {
+ this.getTarget(aWindow, "searchProvider").then(target => {
+ openMenuButton(target.node);
+ }).catch(log.error);
+ }
+ },
+
+ hideMenu: function(aWindow, aMenuName) {
+ function closeMenuButton(aMenuBtn) {
+ if (aMenuBtn && aMenuBtn.boxObject)
+ aMenuBtn.boxObject.openMenu(false);
+ }
+
+ if (aMenuName == "appMenu") {
+ aWindow.PanelUI.hide();
+ } else if (aMenuName == "bookmarks") {
+ let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
+ closeMenuButton(menuBtn);
+ } else if (aMenuName == "loop") {
+ let panel = aWindow.document.getElementById("loop-notification-panel");
+ panel.hidePopup();
+ } else if (aMenuName == "searchEngines") {
+ let menuBtn = this.targets.get("searchProvider").query(aWindow.document);
+ closeMenuButton(menuBtn);
+ }
+ },
+
+ hideAnnotationsForPanel: function(aEvent, aTargetPositionCallback) {
+ let win = aEvent.target.ownerDocument.defaultView;
+ let annotationElements = new Map([
+ // [annotationElement (panel), method to hide the annotation]
+ [win.document.getElementById("UITourHighlightContainer"), UITour.hideHighlight.bind(UITour)],
+ [win.document.getElementById("UITourTooltip"), UITour.hideInfo.bind(UITour)],
+ ]);
+ annotationElements.forEach((hideMethod, annotationElement) => {
+ if (annotationElement.state != "closed") {
+ let targetName = annotationElement.getAttribute("targetName");
+ UITour.getTarget(win, targetName).then((aTarget) => {
+ // Since getTarget is async, we need to make sure that the target hasn't
+ // changed since it may have just moved to somewhere outside of the app menu.
+ if (annotationElement.getAttribute("targetName") != aTarget.targetName ||
+ annotationElement.state == "closed" ||
+ !aTargetPositionCallback(aTarget)) {
+ return;
+ }
+ hideMethod(win);
+ }).catch(log.error);
+ }
+ });
+ UITour.appMenuOpenForAnnotation.clear();
+ },
+
+ hideAppMenuAnnotations: function(aEvent) {
+ UITour.hideAnnotationsForPanel(aEvent, UITour.targetIsInAppMenu);
+ },
+
+ hideLoopPanelAnnotations: function(aEvent) {
+ UITour.hideAnnotationsForPanel(aEvent, (aTarget) => {
+ return aTarget.targetName.startsWith("loop-") && aTarget.targetName != "loop-selectedRoomButtons";
+ });
+ },
+
+ onPanelHidden: function(aEvent) {
+ aEvent.target.removeAttribute("noautohide");
+ UITour.recreatePopup(aEvent.target);
+ },
+
+ recreatePopup: function(aPanel) {
+ // After changing popup attributes that relate to how the native widget is created
+ // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect.
+ if (aPanel.hidden) {
+ // If the panel is already hidden, we don't need to recreate it but flush
+ // in case someone just hid it.
+ aPanel.clientWidth; // flush
+ return;
+ }
+ aPanel.hidden = true;
+ aPanel.clientWidth; // flush
+ aPanel.hidden = false;
+ },
+
+ startUrlbarCapture: function(aWindow, aExpectedText, aUrl) {
+ let urlbar = aWindow.document.getElementById("urlbar");
+ this.urlbarCapture.set(aWindow, {
+ expected: aExpectedText.toLocaleLowerCase(),
+ url: aUrl
+ });
+ urlbar.addEventListener("input", this);
+ },
+
+ endUrlbarCapture: function(aWindow) {
+ let urlbar = aWindow.document.getElementById("urlbar");
+ urlbar.removeEventListener("input", this);
+ this.urlbarCapture.delete(aWindow);
+ },
+
+ handleUrlbarInput: function(aWindow) {
+ if (!this.urlbarCapture.has(aWindow))
+ return;
+
+ let urlbar = aWindow.document.getElementById("urlbar");
+
+ let {expected, url} = this.urlbarCapture.get(aWindow);
+
+ if (urlbar.value.toLocaleLowerCase().localeCompare(expected) != 0)
+ return;
+
+ urlbar.handleRevert();
+
+ let tab = aWindow.gBrowser.addTab(url, {
+ owner: aWindow.gBrowser.selectedTab,
+ relatedToCurrent: true
+ });
+ aWindow.gBrowser.selectedTab = tab;
+ },
+
+ getConfiguration: function(aMessageManager, aWindow, aConfiguration, aCallbackID) {
+ switch (aConfiguration) {
+ case "availableTargets":
+ this.getAvailableTargets(aMessageManager, aWindow, aCallbackID);
+ break;
+ case "sync":
+ this.sendPageCallback(aMessageManager, aCallbackID, {
+ setup: Services.prefs.prefHasUserValue("services.sync.username"),
+ });
+ break;
+ case "appinfo":
+ let props = ["defaultUpdateChannel", "version"];
+ let appinfo = {};
+ props.forEach(property => appinfo[property] = Services.appinfo[property]);
+ this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
+ break;
+ case "selectedSearchEngine":
+ Services.search.init(rv => {
+ let engine;
+ if (Components.isSuccessCode(rv)) {
+ engine = Services.search.defaultEngine;
+ } else {
+ engine = { identifier: "" };
+ }
+ this.sendPageCallback(aMessageManager, aCallbackID, {
+ searchEngineIdentifier: engine.identifier
+ });
+ });
+ break;
+ default:
+ log.error("getConfiguration: Unknown configuration requested: " + aConfiguration);
+ break;
+ }
+ },
+
+ setConfiguration: function(aConfiguration, aValue) {
+ switch (aConfiguration) {
+ case "Loop:ResumeTourOnFirstJoin":
+ // Ignore aValue in this case to avoid accidentally setting it to false.
+ Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
+ break;
+ default:
+ log.error("setConfiguration: Unknown configuration requested: " + aConfiguration);
+ break;
+ }
+ },
+
+ getAvailableTargets: function(aMessageManager, aChromeWindow, aCallbackID) {
+ Task.spawn(function*() {
+ let window = aChromeWindow;
+ let data = this.availableTargetsCache.get(window);
+ if (data) {
+ log.debug("getAvailableTargets: Using cached targets list", data.targets.join(","));
+ this.sendPageCallback(aMessageManager, aCallbackID, data);
+ return;
+ }
+
+ let promises = [];
+ for (let targetName of this.targets.keys()) {
+ promises.push(this.getTarget(window, targetName));
+ }
+ let targetObjects = yield Promise.all(promises);
+
+ let targetNames = [
+ "pinnedTab",
+ ];
+
+ for (let targetObject of targetObjects) {
+ if (targetObject.node)
+ targetNames.push(targetObject.targetName);
+ }
+
+ targetNames = targetNames.concat(
+ yield this.getAvailableSearchEngineTargets(window)
+ );
+
+ data = {
+ targets: targetNames,
+ };
+ this.availableTargetsCache.set(window, data);
+ this.sendPageCallback(aMessageManager, aCallbackID, data);
+ }.bind(this)).catch(err => {
+ log.error(err);
+ this.sendPageCallback(aMessageManager, aCallbackID, {
+ targets: [],
+ });
+ });
+ },
+
+ addNavBarWidget: function (aTarget, aMessageManager, aCallbackID) {
+ if (aTarget.node) {
+ log.error("addNavBarWidget: can't add a widget already present:", aTarget);
+ return;
+ }
+ if (!aTarget.allowAdd) {
+ log.error("addNavBarWidget: not allowed to add this widget:", aTarget);
+ return;
+ }
+ if (!aTarget.widgetName) {
+ log.error("addNavBarWidget: can't add a widget without a widgetName property:", aTarget);
+ return;
+ }
+
+ CustomizableUI.addWidgetToArea(aTarget.widgetName, CustomizableUI.AREA_NAVBAR);
+ this.sendPageCallback(aMessageManager, aCallbackID);
+ },
+
+ _addAnnotationPanelMutationObserver: function(aPanelEl) {
+#ifdef XP_LINUX
+ let observer = this._annotationPanelMutationObservers.get(aPanelEl);
+ if (observer) {
+ return;
+ }
+ let win = aPanelEl.ownerDocument.defaultView;
+ observer = new win.MutationObserver(this._annotationMutationCallback);
+ this._annotationPanelMutationObservers.set(aPanelEl, observer);
+ let observerOptions = {
+ attributeFilter: ["height", "width"],
+ attributes: true,
+ };
+ observer.observe(aPanelEl, observerOptions);
+#endif
+ },
+
+ _removeAnnotationPanelMutationObserver: function(aPanelEl) {
+#ifdef XP_LINUX
+ let observer = this._annotationPanelMutationObservers.get(aPanelEl);
+ if (observer) {
+ observer.disconnect();
+ this._annotationPanelMutationObservers.delete(aPanelEl);
+ }
+#endif
+ },
+
+/**
+ * Workaround for Ubuntu panel craziness in bug 970788 where incorrect sizes get passed to
+ * nsXULPopupManager::PopupResized and lead to incorrect width and height attributes getting
+ * set on the panel.
+ */
+ _annotationMutationCallback: function(aMutations) {
+ for (let mutation of aMutations) {
+ // Remove both attributes at once and ignore remaining mutations to be proccessed.
+ mutation.target.removeAttribute("width");
+ mutation.target.removeAttribute("height");
+ return;
+ }
+ },
+
+ selectSearchEngine(aID) {
+ return new Promise((resolve, reject) => {
+ Services.search.init((rv) => {
+ if (!Components.isSuccessCode(rv)) {
+ reject("selectSearchEngine: search service init failed: " + rv);
+ return;
+ }
+
+ let engines = Services.search.getVisibleEngines();
+ for (let engine of engines) {
+ if (engine.identifier == aID) {
+ Services.search.defaultEngine = engine;
+ return resolve();
+ }
+ }
+ reject("selectSearchEngine could not find engine with given ID");
+ });
+ });
+ },
+
+ getAvailableSearchEngineTargets(aWindow) {
+ return new Promise(resolve => {
+ this.getTarget(aWindow, "search").then(searchTarget => {
+ if (!searchTarget.node || this.targetIsInAppMenu(searchTarget))
+ return resolve([]);
+
+ Services.search.init(() => {
+ let engines = Services.search.getVisibleEngines();
+ resolve([TARGET_SEARCHENGINE_PREFIX + engine.identifier
+ for (engine of engines)
+ if (engine.identifier)]);
+ });
+ }).catch(() => resolve([]));
+ });
+ },
+
+ // We only allow matching based on a search engine's identifier - this gives
+ // us a non-changing ID and guarentees we only match against app-provided
+ // engines.
+ getSearchEngineTarget(aWindow, aIdentifier) {
+ return new Promise((resolve, reject) => {
+ Task.spawn(function*() {
+ let searchTarget = yield this.getTarget(aWindow, "search");
+ // We're not supporting having the searchbar in the app-menu, because
+ // popups within popups gets crazy. This restriction should be lifted
+ // once bug 988151 is implemented, as the page can then be responsible
+ // for opening each menu when appropriate.
+ if (!searchTarget.node || this.targetIsInAppMenu(searchTarget))
+ return reject("Search engine not available");
+
+ yield Services.search.init();
+
+ let searchPopup = searchTarget.node._popup;
+ for (let engineNode of searchPopup.children) {
+ let engine = engineNode.engine;
+ if (engine && engine.identifier == aIdentifier) {
+ return resolve({
+ targetName: TARGET_SEARCHENGINE_PREFIX + engine.identifier,
+ node: engineNode,
+ });
+ }
+ }
+ reject("Search engine not available");
+ }.bind(this)).catch(() => {
+ reject("Search engine not available");
+ });
+ });
+ },
+
+ notify(eventName, params) {
+ let winEnum = Services.wm.getEnumerator("navigator:browser");
+ while (winEnum.hasMoreElements()) {
+ let window = winEnum.getNext();
+ if (window.closed)
+ continue;
+
+ let originTabs = this.originTabs.get(window);
+ if (!originTabs)
+ continue;
+
+ for (let tab of originTabs) {
+ let messageManager = tab.linkedBrowser.messageManager;
+ let detail = {
+ event: eventName,
+ params: params,
+ };
+ messageManager.sendAsyncMessage("UITour:SendPageNotification", detail);
+ }
+ }
+ },
+};
+
+this.UITour.init();
+
+/**
+ * UITour Health Report
+ */
+const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
+
+/**
+ * Public API to be called by the UITour code
+ */
+const UITourHealthReport = {
+ recordTreatmentTag: function(tag, value) {
+#ifdef MOZ_SERVICES_HEALTHREPORT
+ Task.spawn(function*() {
+ let reporter = Cc["@mozilla.org/datareporting/service;1"]
+ .getService()
+ .wrappedJSObject
+ .healthReporter;
+
+ // This can happen if the FHR component of the data reporting service is
+ // disabled. This is controlled by a pref that most will never use.
+ if (!reporter) {
+ return;
+ }
+
+ yield reporter.onInit();
+
+ // Get the UITourMetricsProvider instance from the Health Reporter
+ reporter.getProvider("org.mozilla.uitour").recordTreatmentTag(tag, value);
+ });
+#endif
+ }
+};
+
+this.UITourMetricsProvider = function() {
+ Metrics.Provider.call(this);
+}
+
+UITourMetricsProvider.prototype = Object.freeze({
+ __proto__: Metrics.Provider.prototype,
+
+ name: "org.mozilla.uitour",
+
+ measurementTypes: [
+ UITourTreatmentMeasurement1,
+ ],
+
+ recordTreatmentTag: function(tag, value) {
+ let m = this.getMeasurement(UITourTreatmentMeasurement1.prototype.name,
+ UITourTreatmentMeasurement1.prototype.version);
+ let field = tag;
+
+ if (this.storage.hasFieldFromMeasurement(m.id, field,
+ DAILY_DISCRETE_TEXT_FIELD)) {
+ let fieldID = this.storage.fieldIDFromMeasurement(m.id, field);
+ return this.enqueueStorageOperation(function recordKnownField() {
+ return this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
+ }.bind(this));
+ }
+
+ // Otherwise, we first need to create the field.
+ return this.enqueueStorageOperation(function recordField() {
+ // This function has to return a promise.
+ return Task.spawn(function () {
+ let fieldID = yield this.storage.registerField(m.id, field,
+ DAILY_DISCRETE_TEXT_FIELD);
+ yield this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
+ }.bind(this));
+ }.bind(this));
+ },
+});
+
+function UITourTreatmentMeasurement1() {
+ Metrics.Measurement.call(this);
+
+ this._serializers = {};
+ this._serializers[this.SERIALIZE_JSON] = {
+ //singular: We don't need a singular serializer because we have none of this data
+ daily: this._serializeJSONDaily.bind(this)
+ };
+
+}
+
+UITourTreatmentMeasurement1.prototype = Object.freeze({
+ __proto__: Metrics.Measurement.prototype,
+
+ name: "treatment",
+ version: 1,
+
+ // our fields are dynamic
+ fields: { },
+
+ // We need a custom serializer because the default one doesn't accept unknown fields
+ _serializeJSONDaily: function(data) {
+ let result = {_v: this.version };
+
+ for (let [field, data] of data) {
+ result[field] = data;
+ }
+
+ return result;
+ }
+});
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/UITour-lib.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/UITour-lib.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/components/uitour/UITour-lib.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/components/uitour/UITour-lib.js 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,280 @@
+/* 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/. */
+
+// create namespace
+if (typeof Mozilla == 'undefined') {
+ var Mozilla = {};
+}
+
+;(function($) {
+ 'use strict';
+
+ // create namespace
+ if (typeof Mozilla.UITour == 'undefined') {
+ Mozilla.UITour = {};
+ }
+
+ var themeIntervalId = null;
+ function _stopCyclingThemes() {
+ if (themeIntervalId) {
+ clearInterval(themeIntervalId);
+ themeIntervalId = null;
+ }
+ }
+
+ function _sendEvent(action, data) {
+ var event = new CustomEvent('mozUITour', {
+ bubbles: true,
+ detail: {
+ action: action,
+ data: data || {}
+ }
+ });
+
+ document.dispatchEvent(event);
+ }
+
+ function _generateCallbackID() {
+ return Math.random().toString(36).replace(/[^a-z]+/g, '');
+ }
+
+ function _waitForCallback(callback) {
+ var id = _generateCallbackID();
+
+ function listener(event) {
+ if (typeof event.detail != 'object')
+ return;
+ if (event.detail.callbackID != id)
+ return;
+
+ document.removeEventListener('mozUITourResponse', listener);
+ callback(event.detail.data);
+ }
+ document.addEventListener('mozUITourResponse', listener);
+
+ return id;
+ }
+
+ var notificationListener = null;
+ function _notificationListener(event) {
+ if (typeof event.detail != 'object')
+ return;
+ if (typeof notificationListener != 'function')
+ return;
+
+ notificationListener(event.detail.event, event.detail.params);
+ }
+
+ Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
+
+ Mozilla.UITour.CONFIGNAME_SYNC = 'sync';
+ Mozilla.UITour.CONFIGNAME_AVAILABLETARGETS = 'availableTargets';
+
+ Mozilla.UITour.ping = function(callback) {
+ var data = {};
+ if (callback) {
+ data.callbackID = _waitForCallback(callback);
+ }
+ _sendEvent('ping', data);
+ };
+
+ Mozilla.UITour.observe = function(listener, callback) {
+ notificationListener = listener;
+
+ if (listener) {
+ document.addEventListener('mozUITourNotification',
+ _notificationListener);
+ Mozilla.UITour.ping(callback);
+ } else {
+ document.removeEventListener('mozUITourNotification',
+ _notificationListener);
+ }
+ };
+
+ Mozilla.UITour.registerPageID = function(pageID) {
+ _sendEvent('registerPageID', {
+ pageID: pageID
+ });
+ };
+
+ Mozilla.UITour.showHighlight = function(target, effect) {
+ _sendEvent('showHighlight', {
+ target: target,
+ effect: effect
+ });
+ };
+
+ Mozilla.UITour.hideHighlight = function() {
+ _sendEvent('hideHighlight');
+ };
+
+ Mozilla.UITour.showInfo = function(target, title, text, icon, buttons, options) {
+ var buttonData = [];
+ if (Array.isArray(buttons)) {
+ for (var i = 0; i < buttons.length; i++) {
+ buttonData.push({
+ label: buttons[i].label,
+ icon: buttons[i].icon,
+ style: buttons[i].style,
+ callbackID: _waitForCallback(buttons[i].callback)
+ });
+ }
+ }
+
+ var closeButtonCallbackID, targetCallbackID;
+ if (options && options.closeButtonCallback)
+ closeButtonCallbackID = _waitForCallback(options.closeButtonCallback);
+ if (options && options.targetCallback)
+ targetCallbackID = _waitForCallback(options.targetCallback);
+
+ _sendEvent('showInfo', {
+ target: target,
+ title: title,
+ text: text,
+ icon: icon,
+ buttons: buttonData,
+ closeButtonCallbackID: closeButtonCallbackID,
+ targetCallbackID: targetCallbackID
+ });
+ };
+
+ Mozilla.UITour.hideInfo = function() {
+ _sendEvent('hideInfo');
+ };
+
+ Mozilla.UITour.previewTheme = function(theme) {
+ _stopCyclingThemes();
+
+ _sendEvent('previewTheme', {
+ theme: JSON.stringify(theme)
+ });
+ };
+
+ Mozilla.UITour.resetTheme = function() {
+ _stopCyclingThemes();
+
+ _sendEvent('resetTheme');
+ };
+
+ Mozilla.UITour.cycleThemes = function(themes, delay, callback) {
+ _stopCyclingThemes();
+
+ if (!delay) {
+ delay = Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY;
+ }
+
+ function nextTheme() {
+ var theme = themes.shift();
+ themes.push(theme);
+
+ _sendEvent('previewTheme', {
+ theme: JSON.stringify(theme),
+ state: true
+ });
+
+ callback(theme);
+ }
+
+ themeIntervalId = setInterval(nextTheme, delay);
+ nextTheme();
+ };
+
+ Mozilla.UITour.addPinnedTab = function() {
+ _sendEvent('addPinnedTab');
+ };
+
+ Mozilla.UITour.removePinnedTab = function() {
+ _sendEvent('removePinnedTab');
+ };
+
+ Mozilla.UITour.showMenu = function(name, callback) {
+ var showCallbackID;
+ if (callback)
+ showCallbackID = _waitForCallback(callback);
+
+ _sendEvent('showMenu', {
+ name: name,
+ showCallbackID: showCallbackID,
+ });
+ };
+
+ Mozilla.UITour.hideMenu = function(name) {
+ _sendEvent('hideMenu', {
+ name: name
+ });
+ };
+
+ Mozilla.UITour.startUrlbarCapture = function(text, url) {
+ _sendEvent('startUrlbarCapture', {
+ text: text,
+ url: url
+ });
+ };
+
+ Mozilla.UITour.endUrlbarCapture = function() {
+ _sendEvent('endUrlbarCapture');
+ };
+
+ Mozilla.UITour.getConfiguration = function(configName, callback) {
+ _sendEvent('getConfiguration', {
+ callbackID: _waitForCallback(callback),
+ configuration: configName,
+ });
+ };
+
+ Mozilla.UITour.setConfiguration = function(configName, configValue) {
+ _sendEvent('setConfiguration', {
+ configuration: configName,
+ value: configValue,
+ });
+ };
+
+ Mozilla.UITour.showFirefoxAccounts = function() {
+ _sendEvent('showFirefoxAccounts');
+ };
+
+ Mozilla.UITour.resetFirefox = function() {
+ _sendEvent('resetFirefox');
+ };
+
+ Mozilla.UITour.addNavBarWidget= function(name, callback) {
+ _sendEvent('addNavBarWidget', {
+ name: name,
+ callbackID: _waitForCallback(callback),
+ });
+ };
+
+ Mozilla.UITour.setDefaultSearchEngine = function(identifier) {
+ _sendEvent('setDefaultSearchEngine', {
+ identifier: identifier,
+ });
+ };
+
+ Mozilla.UITour.setTreatmentTag = function(name, value) {
+ _sendEvent('setTreatmentTag', {
+ name: name,
+ value: value
+ });
+ };
+
+ Mozilla.UITour.getTreatmentTag = function(name, callback) {
+ _sendEvent('getTreatmentTag', {
+ name: name,
+ callbackID: _waitForCallback(callback)
+ });
+ };
+
+ Mozilla.UITour.setSearchTerm = function(term) {
+ _sendEvent('setSearchTerm', {
+ term: term
+ });
+ };
+
+ Mozilla.UITour.openSearchPanel = function(callback) {
+ _sendEvent('openSearchPanel', {
+ callbackID: _waitForCallback(callback)
+ });
+ };
+
+})();
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/linux64/debug-static-analysis-clang thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/linux64/debug-static-analysis-clang
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/linux64/debug-static-analysis-clang 2015-01-06 13:14:23.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/linux64/debug-static-analysis-clang 2015-01-07 22:15:20.000000000 +0000
@@ -5,11 +5,10 @@
. "$topsrcdir/build/mozconfig.common"
ac_add_options --enable-debug
+ac_add_options --enable-dmd
ac_add_options --disable-unified-compilation
-ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
-
# Use Clang as specified in manifest
export CC="$topsrcdir/clang/bin/clang"
export CXX="$topsrcdir/clang/bin/clang++"
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/macosx64/debug thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/macosx64/debug
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/macosx64/debug 2015-01-06 13:14:23.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/macosx64/debug 2015-01-07 22:15:20.000000000 +0000
@@ -2,7 +2,6 @@
ac_add_options --enable-debug
ac_add_options --enable-dmd
-ac_add_options --enable-accessibility
ac_add_options --enable-signmar
ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/macosx64/debug-asan thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/macosx64/debug-asan
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/macosx64/debug-asan 2015-01-06 13:14:23.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/macosx64/debug-asan 2015-01-07 22:15:20.000000000 +0000
@@ -3,7 +3,6 @@
ac_add_options --enable-application=browser
ac_add_options --enable-debug
ac_add_options --enable-optimize="-O1"
-ac_add_options --enable-accessibility
ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
ac_add_options --disable-unified-compilation
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/macosx64/debug-static-analysis thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/macosx64/debug-static-analysis
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/config/mozconfigs/macosx64/debug-static-analysis 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/config/mozconfigs/macosx64/debug-static-analysis 2015-01-07 22:15:20.000000000 +0000
@@ -0,0 +1,15 @@
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_PACKAGE_TESTS=0
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. $topsrcdir/build/macosx/mozconfig.common
+
+ac_add_options --enable-debug
+ac_add_options --enable-dmd
+
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
+ac_add_options --enable-clang-plugin
+
+. "$topsrcdir/build/mozconfig.common.override"
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js 2015-01-07 22:15:21.000000000 +0000
@@ -102,15 +102,54 @@
info("Item selected. Waiting for NETWORKDETAILSVIEW_POPULATED");
yield onDetailsPopulated;
- info("Selecting tab at index " + tabIndex);
+ info("Received populated event. Selecting tab at index " + tabIndex);
NetworkDetails.widget.selectedIndex = tabIndex;
- ok(true, "Received NETWORKDETAILSVIEW_POPULATED. Waiting for request to finish");
+ info("Waiting for request to finish.");
yield onRequestFinished;
- ok(true, "Request finished. Waiting for tab update to complete");
- let onDetailsUpdateFinished = waitFor(panel, EVENTS.TAB_UPDATED);
- yield onDetailsUpdateFinished;
- ok(true, "Details were updated");
+ ok(true, "Request finished.");
+
+ /**
+ * Because this test uses lazy updates there's four scenarios to consider:
+ * #1: Everything is updated and test is ready to continue.
+ * #2: There's updates that are waiting to be flushed.
+ * #3: Updates are flushed but the tab update is still running.
+ * #4: There's pending updates and a tab update is still running.
+ *
+ * For case #1 there's not going to be a TAB_UPDATED event so don't wait for
+ * it (bug 1106181).
+ *
+ * For cases #2 and #3 it's enough to wait for one TAB_UPDATED event as for
+ * - case #2 the next flush will perform the final update and single
+ * TAB_UPDATED event is emitted.
+ * - case #3 the running update is the final update that'll emit one
+ * TAB_UPDATED event.
+ *
+ * For case #4 we must wait for the updates to be flushed before we can
+ * start waiting for TAB_UPDATED event or we'll continue the test right
+ * after the pending update finishes.
+ */
+ let hasQueuedUpdates = RequestsMenu._updateQueue.length !== 0;
+ let hasRunningTabUpdate = NetworkDetails._viewState.updating[tabIndex];
+
+ if (hasQueuedUpdates || hasRunningTabUpdate) {
+ info("There's pending updates - waiting for them to finish.");
+ info(" hasQueuedUpdates: " + hasQueuedUpdates);
+ info(" hasRunningTabUpdate: " + hasRunningTabUpdate);
+
+ if (hasQueuedUpdates && hasRunningTabUpdate) {
+ info("Waiting for updates to be flushed.");
+ // _flushRequests calls .populate which emits the following event
+ yield waitFor(panel, EVENTS.NETWORKDETAILSVIEW_POPULATED);
+
+ info("Requests flushed.");
+ }
+
+ info("Waiting for final tab update.");
+ yield waitFor(panel, EVENTS.TAB_UPDATED);
+ }
+
+ info("All updates completed.");
}
});
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/performance/views/overview.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/performance/views/overview.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/performance/views/overview.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/performance/views/overview.js 2015-01-07 22:15:21.000000000 +0000
@@ -69,7 +69,9 @@
* Sets up the framerate graph.
*/
_showFramerateGraph: Task.async(function *() {
- this.framerateGraph = new LineGraphWidget($("#time-framerate"), L10N.getStr("graphs.fps"));
+ this.framerateGraph = new LineGraphWidget($("#time-framerate"), {
+ metric: L10N.getStr("graphs.fps")
+ });
this.framerateGraph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
yield this.framerateGraph.ready();
}),
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/profiler/ui-profile.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/profiler/ui-profile.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/profiler/ui-profile.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/profiler/ui-profile.js 2015-01-07 22:15:21.000000000 +0000
@@ -392,7 +392,9 @@
return null;
}
- let graph = new LineGraphWidget($(".framerate", panel), L10N.getStr("graphs.fps"));
+ let graph = new LineGraphWidget($(".framerate", panel), {
+ metric: L10N.getStr("graphs.fps")
+ });
graph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
graph.dataOffsetX = beginAt;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/plugins/rename/rename.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/plugins/rename/rename.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/plugins/rename/rename.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/plugins/rename/rename.js 2015-01-07 22:15:21.000000000 +0000
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=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/. */
+
+const { Class } = require("sdk/core/heritage");
+const { registerPlugin, Plugin } = require("projecteditor/plugins/core");
+const { getLocalizedString } = require("projecteditor/helpers/l10n");
+
+var RenamePlugin = Class({
+ extends: Plugin,
+
+ init: function(host) {
+ this.host.addCommand(this, {
+ id: "cmd-rename"
+ });
+ this.contextMenuItem = this.host.createMenuItem({
+ parent: this.host.contextMenuPopup,
+ label: getLocalizedString("projecteditor.renameLabel"),
+ command: "cmd-rename"
+ });
+ },
+
+ onCommand: function(cmd) {
+ if (cmd === "cmd-rename") {
+ let tree = this.host.projectTree;
+ let resource = tree.getSelectedResource();
+ let parent = resource.parent;
+ let oldName = resource.basename;
+
+ tree.promptEdit(oldName, resource).then(name => {
+ if (name === oldName) {
+ return resource;
+ }
+ if (resource.hasChild(parent, name)) {
+ let matches = name.match(/([^\d.]*)(\d*)([^.]*)(.*)/);
+ let template = matches[1] + "{1}" + matches[3] + matches[4];
+ name = this.suggestName(resource, template, parseInt(matches[2]) || 2);
+ }
+ return parent.rename(oldName,name);
+ }).then(resource => {
+ this.host.project.refresh();
+ tree.selectResource(resource);
+ if (!resource.isDir) {
+ this.host.currentEditor.focus();
+ }
+ }).then(null, console.error);
+ }
+ },
+
+ suggestName: function(resource, template, start=1) {
+ let i = start;
+ let name;
+ let parent = resource.parent;
+ do {
+ name = template.replace("\{1\}", i === 1 ? "" : i);
+ i++;
+ } while (resource.hasChild(parent, name));
+
+ return name;
+ }
+});
+
+exports.RenamePlugin = RenamePlugin;
+registerPlugin(RenamePlugin);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/projecteditor.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/projecteditor.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/projecteditor.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/projecteditor.js 2015-01-07 22:15:21.000000000 +0000
@@ -27,6 +27,7 @@
require("projecteditor/plugins/dirty/dirty");
require("projecteditor/plugins/delete/delete");
require("projecteditor/plugins/new/new");
+require("projecteditor/plugins/rename/rename");
require("projecteditor/plugins/save/save");
require("projecteditor/plugins/image-view/plugin");
require("projecteditor/plugins/app-manager/plugin");
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/stores/resource.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/stores/resource.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/stores/resource.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/stores/resource.js 2015-01-07 22:15:21.000000000 +0000
@@ -127,6 +127,21 @@
},
/**
+ * Checks a resource has child with specific name.
+ *
+ * @param Resource resource
+ * @param string name
+ */
+ hasChild: function(resource, name) {
+ for (let child of resource.children) {
+ if (child.basename === name) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
* Remove a resource to children set and notify of the change.
*
* @param Resource resource
@@ -296,6 +311,27 @@
return this.store.refresh();
}).then(() => {
let resource = this.store.resources.get(newPath);
+ if (!resource) {
+ throw new Error("Error creating " + newPath);
+ }
+ return resource;
+ });
+ },
+
+ /**
+ * Rename the file from the filesystem
+ *
+ * @returns Promise
+ * Resolves with the renamed FileResource.
+ */
+ rename: function(oldName, newName) {
+ let oldPath = OS.Path.join(this.path, oldName);
+ let newPath = OS.Path.join(this.path, newName);
+
+ return OS.File.move(oldPath, newPath).then(() => {
+ return this.store.refresh();
+ }).then(() => {
+ let resource = this.store.resources.get(newPath);
if (!resource) {
throw new Error("Error creating " + newPath);
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/tree.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/tree.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/lib/tree.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/lib/tree.js 2015-01-07 22:15:21.000000000 +0000
@@ -277,6 +277,39 @@
},
/**
+ * Prompt the user to rename file in the tree.
+ *
+ * @param string initial
+ * The suggested starting file name
+ * @param resource
+ *
+ * @returns Promise
+ * Resolves once the prompt has been successful,
+ * Rejected if it is cancelled
+ */
+ promptEdit: function(initial, resource) {
+ let deferred = promise.defer();
+ let placeholder = this._containers.get(resource).elt;
+
+ new InplaceEditor({
+ element: placeholder,
+ initial: initial,
+ start: editor => {
+ editor.input.select();
+ },
+ done: function(val, commit) {
+ if (commit) {
+ deferred.resolve(val);
+ } else {
+ deferred.reject(val);
+ }
+ },
+ });
+
+ return deferred.promise;
+ },
+
+ /**
* Add a new Store into the TreeView
*
* @param Store model
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/moz.build thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/moz.build
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/moz.build 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/moz.build 2015-01-07 22:15:21.000000000 +0000
@@ -52,6 +52,10 @@
'lib/plugins/new/new.js',
]
+EXTRA_JS_MODULES.devtools.projecteditor.plugins.rename += [
+ 'lib/plugins/rename/rename.js',
+]
+
EXTRA_JS_MODULES.devtools.projecteditor.plugins.save += [
'lib/plugins/save/save.js',
]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/test/browser.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/test/browser.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/test/browser.ini 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/test/browser.ini 2015-01-07 22:15:21.000000000 +0000
@@ -12,6 +12,7 @@
[browser_projecteditor_contextmenu_01.js]
[browser_projecteditor_contextmenu_02.js]
[browser_projecteditor_delete_file.js]
+[browser_projecteditor_rename_file.js]
skip-if = buildapp == 'mulet'
[browser_projecteditor_editing_01.js]
skip-if = buildapp == 'mulet'
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/test/browser_projecteditor_rename_file.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/test/browser_projecteditor_rename_file.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/projecteditor/test/browser_projecteditor_rename_file.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/projecteditor/test/browser_projecteditor_rename_file.js 2015-01-07 22:15:21.000000000 +0000
@@ -0,0 +1,59 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test file rename functionality
+
+add_task(function*() {
+ let projecteditor = yield addProjectEditorTabForTempDirectory();
+ ok(true, "ProjectEditor has loaded");
+
+ let root = [...projecteditor.project.allStores()][0].root;
+ is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
+ for (let child of root.children) {
+ yield renameWithContextMenu(projecteditor, projecteditor.projectTree.getViewContainer(child));
+ }
+});
+
+function openContextMenuOn(node) {
+ EventUtils.synthesizeMouseAtCenter(
+ node,
+ {button: 2, type: "contextmenu"},
+ node.ownerDocument.defaultView
+ );
+}
+
+function renameWithContextMenu(projecteditor, container) {
+ let defer = promise.defer();
+ let popup = projecteditor.contextMenuPopup;
+ let resource = container.resource;
+ info ("Going to attempt renaming for: " + resource.path);
+
+ onPopupShow(popup).then(function () {
+ let renameCommand = popup.querySelector("[command=cmd-rename]");
+ ok (renameCommand, "Rename command exists in popup");
+ is (renameCommand.getAttribute("hidden"), "", "Rename command is visible");
+ is (renameCommand.getAttribute("disabled"), "", "Rename command is enabled");
+
+ projecteditor.project.on("refresh-complete", function refreshComplete() {
+ projecteditor.project.off("refresh-complete", refreshComplete);
+ OS.File.stat(resource.path + ".renamed").then(() => {
+ ok (true, "File is renamed");
+ defer.resolve();
+ }, (ex) => {
+ ok (false, "Failed to rename file");
+ defer.resolve();
+ });
+ });
+
+ renameCommand.click();
+ popup.hidePopup();
+ EventUtils.sendString(resource.basename + ".renamed", projecteditor.window);
+ EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window);
+ });
+
+ openContextMenuOn(container.label);
+ return defer.promise;
+}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser_css_color.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser_css_color.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser_css_color.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser_css_color.js 2015-01-07 22:15:21.000000000 +0000
@@ -53,6 +53,7 @@
testColorMatch(name, hex, hsl, rgb, color.rgba);
}
testProcessCSSString();
+ testSetAlpha();
finishUp();
}
@@ -138,6 +139,29 @@
is(after, expected, "CSS string processed correctly");
}
+function testSetAlpha() {
+ let values = [
+ ["longhex", "#ff0000", 0.5, "rgba(255, 0, 0, 0.5)"],
+ ["hex", "#f0f", 0.2, "rgba(255, 0, 255, 0.2)"],
+ ["rgba", "rgba(120, 34, 23, 1)", 0.25, "rgba(120, 34, 23, 0.25)"],
+ ["rgb", "rgb(120, 34, 23)", 0.25, "rgba(120, 34, 23, 0.25)"],
+ ["hsl", "hsl(208, 100%, 97%)", 0.75, "rgba(239, 247, 255, 0.75)"],
+ ["hsla", "hsla(208, 100%, 97%, 1)", 0.75, "rgba(239, 247, 255, 0.75)"]
+ ];
+ values.forEach(([type, value, alpha, expected]) => {
+ is(colorUtils.setAlpha(value, alpha), expected, "correctly sets alpha value for " + type);
+ });
+
+ try {
+ colorUtils.setAlpha("rgb(24, 25, 45, 1)", 1);
+ ok(false, "Should fail when passing in an invalid color.");
+ } catch (e) {
+ ok(true, "Fails when setAlpha receives an invalid color.");
+ }
+
+ is(colorUtils.setAlpha("#fff"), "rgba(255, 255, 255, 1)", "sets alpha to 1 if invalid.");
+}
+
function finishUp() {
Services = colorUtils = Loader = null;
gBrowser.removeCurrentTab();
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser_graphs-09a.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser_graphs-09a.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser_graphs-09a.js 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser_graphs-09a.js 2015-01-07 22:15:21.000000000 +0000
@@ -18,7 +18,7 @@
function* performTest() {
let [host, win, doc] = yield createHost();
- let graph = new LineGraphWidget(doc.body, "fps");
+ let graph = new LineGraphWidget(doc.body, { metric: "fps" });
yield testGraph(graph);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser_graphs-09f.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser_graphs-09f.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser_graphs-09f.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser_graphs-09f.js 2015-01-07 22:15:21.000000000 +0000
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the constructor options for `min`, `max` and `avg` on displaying the
+// gutter/tooltips and lines.
+
+const TEST_DATA = [{ delta: 100, value: 60 }, { delta: 200, value: 1 }];
+let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
+let {DOMHelpers} = Cu.import("resource:///modules/devtools/DOMHelpers.jsm", {});
+let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
+let {Hosts} = devtools.require("devtools/framework/toolbox-hosts");
+
+let test = Task.async(function*() {
+ yield promiseTab("about:blank");
+ yield performTest();
+ gBrowser.removeCurrentTab();
+ finish();
+});
+
+function* performTest() {
+ let [host, win, doc] = yield createHost();
+
+ yield testGraph(doc.body, { avg: false });
+ yield testGraph(doc.body, { min: false });
+ yield testGraph(doc.body, { max: false });
+ yield testGraph(doc.body, { min: false, max: false, avg: false });
+ yield testGraph(doc.body, {});
+
+ host.destroy();
+}
+
+function* testGraph (parent, options) {
+ options.metric = "fps";
+ let graph = new LineGraphWidget(parent, options);
+ yield graph.setDataWhenReady(TEST_DATA);
+ let shouldGutterShow = options.min === false && options.max === false;
+
+ is(graph._gutter.hidden, shouldGutterShow,
+ `The gutter should ${shouldGutterShow ? "" : "not "}be shown`);
+
+ is(graph._maxTooltip.hidden, options.max === false,
+ `The max tooltip should ${options.max === false ? "not " : ""}be shown`);
+ is(graph._maxGutterLine.hidden, options.max === false,
+ `The max gutter should ${options.max === false ? "not " : ""}be shown`);
+ is(graph._minTooltip.hidden, options.min === false,
+ `The min tooltip should ${options.min === false ? "not " : ""}be shown`);
+ is(graph._minGutterLine.hidden, options.min === false,
+ `The min gutter should ${options.min === false ? "not " : ""}be shown`);
+ is(graph._avgTooltip.hidden, options.avg === false,
+ `The avg tooltip should ${options.avg === false ? "not " : ""}be shown`);
+ is(graph._avgGutterLine.hidden, options.avg === false,
+ `The avg gutter should ${options.avg === false ? "not " : ""}be shown`);
+
+ graph.destroy();
+}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/test/browser.ini 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/test/browser.ini 2015-01-07 22:15:21.000000000 +0000
@@ -29,6 +29,7 @@
[browser_graphs-09c.js]
[browser_graphs-09d.js]
[browser_graphs-09e.js]
+[browser_graphs-09f.js]
[browser_graphs-10a.js]
[browser_graphs-10b.js]
[browser_graphs-11a.js]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/widgets/Graphs.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/widgets/Graphs.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/shared/widgets/Graphs.jsm 2015-01-06 13:14:24.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/shared/widgets/Graphs.jsm 2015-01-07 22:15:21.000000000 +0000
@@ -1169,20 +1169,32 @@
*
* @param nsIDOMNode parent
* The parent node holding the graph.
- * @param string metric [optional]
- * The metric displayed in the graph, e.g. "fps" or "bananas".
+ * @param object options [optional]
+ * `metric`: The metric displayed in the graph, e.g. "fps" or "bananas".
+ * `min`: Boolean whether to show the min tooltip/gutter/line (default: true)
+ * `max`: Boolean whether to show the max tooltip/gutter/line (default: true)
+ * `avg`: Boolean whether to show the avg tooltip/gutter/line (default: true)
*/
-this.LineGraphWidget = function(parent, metric, ...args) {
+this.LineGraphWidget = function(parent, options, ...args) {
+ options = options || {};
+ let metric = options.metric;
+
+ this._showMin = options.min !== false;
+ this._showMax = options.max !== false;
+ this._showAvg = options.avg !== false;
AbstractCanvasGraph.apply(this, [parent, "line-graph", ...args]);
this.once("ready", () => {
+ // Create all gutters and tooltips incase the showing of min/max/avg
+ // are changed later
this._gutter = this._createGutter();
+
this._maxGutterLine = this._createGutterLine("maximum");
- this._avgGutterLine = this._createGutterLine("average");
- this._minGutterLine = this._createGutterLine("minimum");
this._maxTooltip = this._createTooltip("maximum", "start", "max", metric);
- this._avgTooltip = this._createTooltip("average", "end", "avg", metric);
+ this._minGutterLine = this._createGutterLine("minimum");
this._minTooltip = this._createTooltip("minimum", "start", "min", metric);
+ this._avgGutterLine = this._createGutterLine("average");
+ this._avgTooltip = this._createTooltip("average", "end", "avg", metric);
});
};
@@ -1362,37 +1374,40 @@
let totalTicks = this._data.length;
// Draw the maximum value horizontal line.
-
- ctx.strokeStyle = this.maximumLineColor;
- ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
- ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
- ctx.beginPath();
- let maximumY = height - maxValue * dataScaleY;
- ctx.moveTo(0, maximumY);
- ctx.lineTo(width, maximumY);
- ctx.stroke();
+ if (this._showMax) {
+ ctx.strokeStyle = this.maximumLineColor;
+ ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
+ ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
+ ctx.beginPath();
+ let maximumY = height - maxValue * dataScaleY;
+ ctx.moveTo(0, maximumY);
+ ctx.lineTo(width, maximumY);
+ ctx.stroke();
+ }
// Draw the average value horizontal line.
-
- ctx.strokeStyle = this.averageLineColor;
- ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
- ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
- ctx.beginPath();
- let averageY = height - avgValue * dataScaleY;
- ctx.moveTo(0, averageY);
- ctx.lineTo(width, averageY);
- ctx.stroke();
+ if (this._showAvg) {
+ ctx.strokeStyle = this.averageLineColor;
+ ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
+ ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
+ ctx.beginPath();
+ let averageY = height - avgValue * dataScaleY;
+ ctx.moveTo(0, averageY);
+ ctx.lineTo(width, averageY);
+ ctx.stroke();
+ }
// Draw the minimum value horizontal line.
-
- ctx.strokeStyle = this.minimumLineColor;
- ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
- ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
- ctx.beginPath();
- let minimumY = height - minValue * dataScaleY;
- ctx.moveTo(0, minimumY);
- ctx.lineTo(width, minimumY);
- ctx.stroke();
+ if (this._showMin) {
+ ctx.strokeStyle = this.minimumLineColor;
+ ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
+ ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
+ ctx.beginPath();
+ let minimumY = height - minValue * dataScaleY;
+ ctx.moveTo(0, minimumY);
+ ctx.lineTo(width, minimumY);
+ ctx.stroke();
+ }
// Update the tooltips text and gutter lines.
@@ -1431,10 +1446,14 @@
this._minTooltip.setAttribute("with-arrows", this.withTooltipArrows);
let distanceMinMax = Math.abs(maxTooltipTop - minTooltipTop);
- this._maxTooltip.hidden = !totalTicks || distanceMinMax < LINE_GRAPH_MIN_MAX_TOOLTIP_DISTANCE;
- this._avgTooltip.hidden = !totalTicks;
- this._minTooltip.hidden = !totalTicks;
- this._gutter.hidden = !totalTicks || !this.withTooltipArrows;
+ this._maxTooltip.hidden = this._showMax === false || !totalTicks || distanceMinMax < LINE_GRAPH_MIN_MAX_TOOLTIP_DISTANCE;
+ this._avgTooltip.hidden = this._showAvg === false || !totalTicks;
+ this._minTooltip.hidden = this._showMin === false || !totalTicks;
+ this._gutter.hidden = (this._showMin === false && this._showMax === false) || !totalTicks || !this.withTooltipArrows;
+
+ this._maxGutterLine.hidden = this._showMax === false;
+ this._avgGutterLine.hidden = this._showAvg === false;
+ this._minGutterLine.hidden = this._showMin === false;
},
/**
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser.ini 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser.ini 2015-01-07 22:15:21.000000000 +0000
@@ -10,6 +10,7 @@
[browser_timeline_overview-initial-selection-01.js]
[browser_timeline_overview-initial-selection-02.js]
[browser_timeline_overview-update.js]
+[browser_timeline_overview-theme.js]
[browser_timeline_panels.js]
[browser_timeline_recording-without-memory.js]
[browser_timeline_recording.js]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser_timeline_filters.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser_timeline_filters.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser_timeline_filters.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser_timeline_filters.js 2015-01-07 22:15:21.000000000 +0000
@@ -48,8 +48,7 @@
yield waitUntil(() => !waterfall._outstandingMarkers.length);
- // A row is 11px. See markers-overview.js
- is(overview.fixedHeight, heightBefore - 11, "Overview is smaller");
+ is(overview.fixedHeight, heightBefore, "Overview height hasn't changed");
ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (2)");
ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (2)");
ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (2)");
@@ -60,7 +59,7 @@
yield waitUntil(() => !waterfall._outstandingMarkers.length);
- is(overview.fixedHeight, heightBefore - 11, "Overview is smaller");
+ is(overview.fixedHeight, heightBefore, "Overview height hasn't changed");
ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (3)");
ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (3)");
ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (3)");
@@ -71,6 +70,7 @@
yield waitUntil(() => !waterfall._outstandingMarkers.length);
+ // A row is 11px. See markers-overview.js
is(overview.fixedHeight, heightBefore - 11, "Overview is smaller");
ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (4)");
ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (4)");
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser_timeline_overview-theme.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser_timeline_overview-theme.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser_timeline_overview-theme.js 1970-01-01 00:00:00.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser_timeline_overview-theme.js 2015-01-07 22:15:21.000000000 +0000
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the markers and memory overviews render with the correct
+ * theme on load, and rerenders when changed.
+ */
+
+const LIGHT_BG = "#fcfcfc";
+const DARK_BG = "#14171a";
+
+add_task(function*() {
+ let { target, panel } = yield initTimelinePanel("about:blank");
+ let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
+
+ $("#memory-checkbox").checked = true;
+
+ setTheme("dark");
+
+ yield TimelineController.updateMemoryRecording();
+ is(TimelineView.markersOverview.backgroundColor, DARK_BG,
+ "correct theme on load for markers.");
+ is(TimelineView.memoryOverview.backgroundColor, DARK_BG,
+ "correct theme on load for memory.");
+
+ yield TimelineController.toggleRecording();
+ ok(true, "Recording has started.");
+
+ yield TimelineController.toggleRecording();
+ ok(true, "Recording has ended.");
+
+ let refreshed = Promise.all([
+ once(TimelineView.markersOverview, "refresh"),
+ once(TimelineView.memoryOverview, "refresh"),
+ ]);
+
+ setTheme("light");
+ yield refreshed;
+
+ ok(true, "Both memory and markers were rerendered after theme change.");
+ is(TimelineView.markersOverview.backgroundColor, LIGHT_BG,
+ "correct theme on after toggle for markers.");
+ is(TimelineView.memoryOverview.backgroundColor, LIGHT_BG,
+ "correct theme on after toggle for memory.");
+
+ refreshed = Promise.all([
+ once(TimelineView.markersOverview, "refresh"),
+ once(TimelineView.memoryOverview, "refresh"),
+ ]);
+
+ setTheme("dark");
+ yield refreshed;
+
+ ok(true, "Both memory and markers were rerendered after theme change.");
+ is(TimelineView.markersOverview.backgroundColor, DARK_BG,
+ "correct theme on after toggle for markers once more.");
+ is(TimelineView.memoryOverview.backgroundColor, DARK_BG,
+ "correct theme on after toggle for memory once more.");
+
+ refreshed = Promise.all([
+ once(TimelineView.markersOverview, "refresh"),
+ once(TimelineView.memoryOverview, "refresh"),
+ ]);
+
+ // Set theme back to light
+ setTheme("light");
+ yield refreshed;
+});
+
+/**
+ * Mimics selecting the theme selector in the toolbox;
+ * sets the preference and emits an event on gDevTools to trigger
+ * the themeing.
+ */
+function setTheme (newTheme) {
+ let oldTheme = Services.prefs.getCharPref("devtools.theme");
+ info("Setting `devtools.theme` to \"" + newTheme + "\"");
+ Services.prefs.setCharPref("devtools.theme", newTheme);
+ gDevTools.emit("pref-changed", {
+ pref: "devtools.theme",
+ newValue: newTheme,
+ oldValue: oldTheme
+ });
+}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js 2015-01-07 22:15:21.000000000 +0000
@@ -7,7 +7,7 @@
add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
- let { $, $$, EVENTS, TimelineController, TimelineView } = panel.panelWin;
+ let { $, $$, EVENTS, TimelineController, TimelineView, TIMELINE_BLUEPRINT} = panel.panelWin;
let { L10N } = devtools.require("devtools/timeline/global");
yield TimelineController.toggleRecording();
@@ -39,7 +39,9 @@
bar.click();
let m = markers[i];
- is($("#timeline-waterfall-details .marker-details-type").getAttribute("value"), m.name,
+ let name = TIMELINE_BLUEPRINT[m.name].label;
+
+ is($("#timeline-waterfall-details .marker-details-type").getAttribute("value"), name,
"sidebar title matches markers name");
let printedStartTime = $(".marker-details-start .marker-details-labelvalue").getAttribute("value");
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/timeline.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/timeline.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/timeline.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/timeline.js 2015-01-07 22:15:21.000000000 +0000
@@ -5,9 +5,11 @@
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+Cu.import("resource:///modules/devtools/gDevTools.jsm");
devtools.lazyRequireGetter(this, "promise");
devtools.lazyRequireGetter(this, "EventEmitter",
@@ -98,6 +100,7 @@
this._onMarkers = this._onMarkers.bind(this);
this._onMemory = this._onMemory.bind(this);
this._onFrames = this._onFrames.bind(this);
+
gFront.on("markers", this._onMarkers);
gFront.on("memory", this._onMemory);
gFront.on("frames", this._onFrames);
@@ -293,8 +296,11 @@
this.waterfall = new Waterfall($("#timeline-waterfall"), $("#timeline-pane"), blueprint);
this.markerDetails = new MarkerDetails($("#timeline-waterfall-details"), $("#timeline-waterfall-container > splitter"));
+ this._onThemeChange = this._onThemeChange.bind(this);
this._onSelecting = this._onSelecting.bind(this);
this._onRefresh = this._onRefresh.bind(this);
+
+ gDevTools.on("pref-changed", this._onThemeChange);
this.markersOverview.on("selecting", this._onSelecting);
this.markersOverview.on("refresh", this._onRefresh);
this.markerDetails.on("resize", this._onRefresh);
@@ -303,6 +309,9 @@
this.waterfall.on("selected", this._onMarkerSelected);
this.waterfall.on("unselected", this._onMarkerSelected);
+ let theme = Services.prefs.getCharPref("devtools.theme");
+ this.markersOverview.setTheme(theme);
+
yield this.markersOverview.ready();
yield this.waterfall.recalculateBounds();
@@ -314,6 +323,7 @@
* Destruction function, called when the tool is closed.
*/
destroy: function() {
+ gDevTools.off("pref-changed", this._onThemeChange);
this.markerDetails.off("resize", this._onRefresh);
this.markerDetails.destroy();
this.waterfall.off("selected", this._onMarkerSelected);
@@ -333,7 +343,10 @@
* Shows the memory overview graph.
*/
showMemoryOverview: Task.async(function*() {
+ let theme = Services.prefs.getCharPref("devtools.theme");
+
this.memoryOverview = new MemoryOverview($("#memory-overview"));
+ this.memoryOverview.setTheme(theme);
yield this.memoryOverview.ready();
let interval = TimelineController.getInterval();
@@ -575,6 +588,20 @@
popup.appendChild(menuitem);
}
},
+
+ /*
+ * Called when the developer tools theme changes. Redraws
+ * the graphs with the new theme setting.
+ */
+ _onThemeChange: function (_, theme) {
+ if (this.memoryOverview) {
+ this.memoryOverview.setTheme(theme.newValue);
+ this.memoryOverview.refresh({ force: true });
+ }
+
+ this.markersOverview.setTheme(theme.newValue);
+ this.markersOverview.refresh({ force: true });
+ }
};
/**
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/widgets/global.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/widgets/global.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/widgets/global.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/widgets/global.js 2015-01-07 22:15:21.000000000 +0000
@@ -30,38 +30,38 @@
group: 0,
fill: "hsl(285,50%,68%)",
stroke: "hsl(285,50%,48%)",
- label: L10N.getStr("timeline.label.styles")
+ label: L10N.getStr("timeline.label.styles2")
},
"Reflow": {
- group: 2,
+ group: 0,
+ fill: "hsl(285,50%,68%)",
+ stroke: "hsl(285,50%,48%)",
+ label: L10N.getStr("timeline.label.reflow2")
+ },
+ "Paint": {
+ group: 0,
fill: "hsl(104,57%,71%)",
stroke: "hsl(104,57%,51%)",
- label: L10N.getStr("timeline.label.reflow")
+ label: L10N.getStr("timeline.label.paint")
},
- "Paint": {
+ "DOMEvent": {
group: 1,
fill: "hsl(39,82%,69%)",
stroke: "hsl(39,82%,49%)",
- label: L10N.getStr("timeline.label.paint")
- },
- "DOMEvent": {
- group: 3,
- fill: "hsl(219,82%,69%)",
- stroke: "hsl(219,82%,69%)",
label: L10N.getStr("timeline.label.domevent")
},
+ "Javascript": {
+ group: 1,
+ fill: "hsl(39,82%,69%)",
+ stroke: "hsl(39,82%,49%)",
+ label: L10N.getStr("timeline.label.javascript2")
+ },
"ConsoleTime": {
- group: 4,
+ group: 2,
fill: "hsl(0,0%,80%)",
stroke: "hsl(0,0%,60%)",
label: L10N.getStr("timeline.label.consoleTime")
},
- "Javascript": {
- group: 4,
- fill: "hsl(0,0%,80%)",
- stroke: "hsl(0,0%,60%)",
- label: L10N.getStr("timeline.label.javascript")
- },
};
// Exported symbols.
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/widgets/markers-overview.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/widgets/markers-overview.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/widgets/markers-overview.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/widgets/markers-overview.js 2015-01-07 22:15:21.000000000 +0000
@@ -14,34 +14,29 @@
Cu.import("resource:///modules/devtools/Graphs.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+const { colorUtils: { setAlpha }} = require("devtools/css-color");
+const { getColor } = require("devtools/shared/theme");
+
loader.lazyRequireGetter(this, "L10N",
"devtools/timeline/global", true);
-const HTML_NS = "http://www.w3.org/1999/xhtml";
-
const OVERVIEW_HEADER_HEIGHT = 14; // px
const OVERVIEW_ROW_HEIGHT = 11; // row height
-const OVERVIEW_BACKGROUND_COLOR = "#fff";
-const OVERVIEW_CLIPHEAD_LINE_COLOR = "#666";
-const OVERVIEW_SELECTION_LINE_COLOR = "#555";
-const OVERVIEW_SELECTION_BACKGROUND_COLOR = "rgba(76,158,217,0.25)";
-const OVERVIEW_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
+const OVERVIEW_BODY_HEIGHT = 55; // 11px * 5 groups
+const OVERVIEW_SELECTION_LINE_COLOR = "#666";
+const OVERVIEW_CLIPHEAD_LINE_COLOR = "#555";
const OVERVIEW_HEADER_TICKS_MULTIPLE = 100; // ms
const OVERVIEW_HEADER_TICKS_SPACING_MIN = 75; // px
const OVERVIEW_HEADER_SAFE_BOUNDS = 50; // px
-const OVERVIEW_HEADER_BACKGROUND = "#fff";
-const OVERVIEW_HEADER_TEXT_COLOR = "#18191a";
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
const OVERVIEW_HEADER_TEXT_PADDING_LEFT = 6; // px
const OVERVIEW_HEADER_TEXT_PADDING_TOP = 1; // px
-const OVERVIEW_TIMELINE_STROKES = "#ccc";
const OVERVIEW_MARKERS_COLOR_STOPS = [0, 0.1, 0.75, 1];
const OVERVIEW_MARKER_DURATION_MIN = 4; // ms
const OVERVIEW_GROUP_VERTICAL_PADDING = 5; // px
-const OVERVIEW_GROUP_ALTERNATING_BACKGROUND = "rgba(0,0,0,0.05)";
/**
* An overview for the markers data.
@@ -54,6 +49,8 @@
function MarkersOverview(parent, blueprint, ...args) {
AbstractCanvasGraph.apply(this, [parent, "markers-overview", ...args]);
+ this.setTheme();
+
// Set the list of names, properties and colors used to paint this overview.
this.setBlueprint(blueprint);
@@ -66,8 +63,6 @@
MarkersOverview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
clipheadLineColor: OVERVIEW_CLIPHEAD_LINE_COLOR,
selectionLineColor: OVERVIEW_SELECTION_LINE_COLOR,
- selectionBackgroundColor: OVERVIEW_SELECTION_BACKGROUND_COLOR,
- selectionStripesColor: OVERVIEW_SELECTION_STRIPES_COLOR,
headerHeight: OVERVIEW_HEADER_HEIGHT,
rowHeight: OVERVIEW_ROW_HEIGHT,
groupPadding: OVERVIEW_GROUP_VERTICAL_PADDING,
@@ -138,15 +133,15 @@
// Draw the header and overview background.
- ctx.fillStyle = OVERVIEW_HEADER_BACKGROUND;
+ ctx.fillStyle = this.headerBackgroundColor;
ctx.fillRect(0, 0, canvasWidth, headerHeight);
- ctx.fillStyle = OVERVIEW_BACKGROUND_COLOR;
+ ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, headerHeight, canvasWidth, canvasHeight);
// Draw the alternating odd/even group backgrounds.
- ctx.fillStyle = OVERVIEW_GROUP_ALTERNATING_BACKGROUND;
+ ctx.fillStyle = this.alternatingBackgroundColor;
ctx.beginPath();
for (let i = 0; i < totalGroups; i += 2) {
@@ -166,8 +161,8 @@
ctx.textBaseline = "middle";
ctx.font = fontSize + "px " + fontFamily;
- ctx.fillStyle = OVERVIEW_HEADER_TEXT_COLOR;
- ctx.strokeStyle = OVERVIEW_TIMELINE_STROKES;
+ ctx.fillStyle = this.headerTextColor;
+ ctx.strokeStyle = this.headerTimelineStrokeColor;
ctx.beginPath();
for (let x = 0; x < availableWidth; x += tickInterval) {
@@ -232,6 +227,22 @@
}
return scaledStep;
}
+ },
+
+ /**
+ * Sets the theme via `theme` to either "light" or "dark",
+ * and updates the internal styling to match. Requires a redraw
+ * to see the effects.
+ */
+ setTheme: function (theme) {
+ theme = theme || "light";
+ this.backgroundColor = getColor("body-background", theme);
+ this.selectionBackgroundColor = setAlpha(getColor("selection-background", theme), 0.25);
+ this.selectionStripesColor = setAlpha("#fff", 0.1);
+ this.headerBackgroundColor = getColor("body-background", theme);
+ this.headerTextColor = getColor("body-color", theme);
+ this.headerTimelineStrokeColor = setAlpha(getColor("body-color-alt", theme), 0.1);
+ this.alternatingBackgroundColor = setAlpha(getColor("body-color", theme), 0.05);
}
});
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/widgets/memory-overview.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/widgets/memory-overview.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/timeline/widgets/memory-overview.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/timeline/widgets/memory-overview.js 2015-01-07 22:15:21.000000000 +0000
@@ -13,26 +13,21 @@
Cu.import("resource:///modules/devtools/Graphs.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+const { colorUtils: { setAlpha }} = require("devtools/css-color");
+const { getColor } = require("devtools/shared/theme");
+
loader.lazyRequireGetter(this, "L10N",
"devtools/timeline/global", true);
const HTML_NS = "http://www.w3.org/1999/xhtml";
const OVERVIEW_HEIGHT = 30; // px
-
-const OVERVIEW_BACKGROUND_COLOR = "#fff";
-const OVERVIEW_BACKGROUND_GRADIENT_START = "rgba(0,136,204,0.1)";
-const OVERVIEW_BACKGROUND_GRADIENT_END = "rgba(0,136,204,0.0)";
const OVERVIEW_STROKE_WIDTH = 1; // px
-const OVERVIEW_STROKE_COLOR = "rgba(0,136,204,1)";
-const OVERVIEW_CLIPHEAD_LINE_COLOR = "#666";
-const OVERVIEW_SELECTION_LINE_COLOR = "#555";
const OVERVIEW_MAXIMUM_LINE_COLOR = "rgba(0,136,204,0.4)";
const OVERVIEW_AVERAGE_LINE_COLOR = "rgba(0,136,204,0.7)";
const OVERVIEW_MINIMUM_LINE_COLOR = "rgba(0,136,204,0.9)";
-
-const OVERVIEW_SELECTION_BACKGROUND_COLOR = "rgba(76,158,217,0.25)";
-const OVERVIEW_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
+const OVERVIEW_CLIPHEAD_LINE_COLOR = "#666";
+const OVERVIEW_SELECTION_LINE_COLOR = "#555";
/**
* An overview for the memory data.
@@ -41,8 +36,8 @@
* The parent node holding the overview.
*/
function MemoryOverview(parent) {
- LineGraphWidget.call(this, parent, L10N.getStr("graphs.memory"));
-
+ LineGraphWidget.call(this, parent, { metric: L10N.getStr("graphs.memory") });
+ this.setTheme();
this.once("ready", () => {
// Populate this overview with some dummy initial data.
this.setData({ interval: { startTime: 0, endTime: 1000 }, memory: [] });
@@ -52,18 +47,12 @@
MemoryOverview.prototype = Heritage.extend(LineGraphWidget.prototype, {
dampenValuesFactor: 0.95,
fixedHeight: OVERVIEW_HEIGHT,
- backgroundColor: OVERVIEW_BACKGROUND_COLOR,
- backgroundGradientStart: OVERVIEW_BACKGROUND_GRADIENT_START,
- backgroundGradientEnd: OVERVIEW_BACKGROUND_GRADIENT_END,
- strokeColor: OVERVIEW_STROKE_COLOR,
strokeWidth: OVERVIEW_STROKE_WIDTH,
maximumLineColor: OVERVIEW_MAXIMUM_LINE_COLOR,
averageLineColor: OVERVIEW_AVERAGE_LINE_COLOR,
minimumLineColor: OVERVIEW_MINIMUM_LINE_COLOR,
clipheadLineColor: OVERVIEW_CLIPHEAD_LINE_COLOR,
selectionLineColor: OVERVIEW_SELECTION_LINE_COLOR,
- selectionBackgroundColor: OVERVIEW_SELECTION_BACKGROUND_COLOR,
- selectionStripesColor: OVERVIEW_SELECTION_STRIPES_COLOR,
withTooltipArrows: false,
withFixedTooltipPositions: true,
@@ -82,6 +71,21 @@
setData: function({ interval, memory }) {
this.dataOffsetX = interval.startTime;
LineGraphWidget.prototype.setData.call(this, memory);
+ },
+
+ /**
+ * Sets the theme via `theme` to either "light" or "dark",
+ * and updates the internal styling to match. Requires a redraw
+ * to see the effects.
+ */
+ setTheme: function (theme) {
+ theme = theme || "light";
+ this.backgroundColor = getColor("body-background", theme);
+ this.backgroundGradientStart = setAlpha(getColor("highlight-blue", theme), 0.1);
+ this.backgroundGradientEnd = setAlpha(getColor("highlight-blue", theme), 0);
+ this.strokeColor = getColor("highlight-blue", theme);
+ this.selectionBackgroundColor = setAlpha(getColor("selection-background", theme), 0.25);
+ this.selectionStripesColor = "rgba(255, 255, 255, 0.1)";
}
});
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js 2015-01-07 22:15:21.000000000 +0000
@@ -9,7 +9,7 @@
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
- getN(front, "create-node", 14)
+ getN(front, "create-node", 15)
]);
let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
@@ -17,7 +17,8 @@
"AudioDestinationNode",
"AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode",
"DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode",
- "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode"
+ "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode",
+ "StereoPannerNode"
];
// For some reason nodeTypes.forEach and params.forEach fail here so we use
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js 2015-01-07 22:15:21.000000000 +0000
@@ -9,7 +9,7 @@
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
- getN(front, "create-node", 14)
+ getN(front, "create-node", 15)
]);
let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
@@ -17,7 +17,8 @@
"AudioDestinationNode",
"AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode",
"DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode",
- "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode"
+ "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode",
+ "StereoPannerNode"
];
nodeTypes.forEach((type, i) => {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js 2015-01-07 22:15:21.000000000 +0000
@@ -10,7 +10,7 @@
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
- getN(front, "create-node", 14)
+ getN(front, "create-node", 15)
]);
let allParams = yield Promise.all(nodes.map(node => node.getParams()));
@@ -18,7 +18,7 @@
"AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
"AnalyserNode", "GainNode", "DelayNode", "BiquadFilterNode", "WaveShaperNode",
"PannerNode", "ConvolverNode", "ChannelSplitterNode", "ChannelMergerNode",
- "DynamicsCompressorNode", "OscillatorNode"
+ "DynamicsCompressorNode", "OscillatorNode", "StereoPannerNode"
];
allParams.forEach((params, i) => {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_wa_navigate.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_wa_navigate.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_wa_navigate.js 2015-01-07 22:15:21.000000000 +0000
@@ -25,8 +25,8 @@
navigate(target, SIMPLE_NODES_URL);
var [actors] = yield Promise.all([
- getN(gFront, "create-node", 14),
- waitForGraphRendered(panelWin, 14, 0)
+ getN(gFront, "create-node", 15),
+ waitForGraphRendered(panelWin, 15, 0)
]);
is($("#reload-notice").hidden, true,
@@ -37,7 +37,7 @@
"The tool's content should reappear without closing and reopening the toolbox.");
var { nodes, edges } = countGraphObjects(panelWin);
- ise(nodes, 14, "after navigation, should have 14 nodes");
+ ise(nodes, 15, "after navigation, should have 15 nodes");
ise(edges, 0, "after navigation, should have 0 edges.");
yield teardown(target);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js 2015-01-07 22:15:21.000000000 +0000
@@ -17,8 +17,8 @@
reload(target);
let [actors] = yield Promise.all([
- getN(gFront, "create-node", 14),
- waitForGraphRendered(panelWin, 14, 0)
+ getN(gFront, "create-node", 15),
+ waitForGraphRendered(panelWin, 15, 0)
]);
let nodeIds = actors.map(actor => actor.actorID);
let types = [
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html 2015-01-07 22:15:21.000000000 +0000
@@ -18,7 +18,7 @@
"createBufferSource", "createScriptProcessor", "createAnalyser",
"createGain", "createDelay", "createBiquadFilter", "createWaveShaper",
"createPanner", "createConvolver", "createChannelSplitter", "createChannelMerger",
- "createDynamicsCompressor", "createOscillator"
+ "createDynamicsCompressor", "createOscillator", "createStereoPanner"
];
let nodes = NODE_CREATION_METHODS.map(method => ctx[method]());
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/head.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/head.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/devtools/webaudioeditor/test/head.js 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/devtools/webaudioeditor/test/head.js 2015-01-07 22:15:21.000000000 +0000
@@ -497,5 +497,8 @@
"type": "sine",
"frequency": 440,
"detune": 0
+ },
+ "StereoPannerNode": {
+ "pan": 0
}
};
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/docs/index.rst thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/docs/index.rst
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/docs/index.rst 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/docs/index.rst 2015-01-07 22:15:21.000000000 +0000
@@ -7,3 +7,4 @@
.. toctree::
:maxdepth: 1
+ UITelemetry
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/docs/UITelemetry.rst thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/docs/UITelemetry.rst
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/docs/UITelemetry.rst 2015-01-06 13:14:25.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/docs/UITelemetry.rst 2015-01-07 22:15:21.000000000 +0000
@@ -38,6 +38,9 @@
objects, where each object has a ``duration`` property indicating the time in milliseconds,
and a ``bucket`` property indicating a bucket in which the duration info falls.
+
+.. _UITelemetry_countableEvents:
+
``countableEvents``
-------------------
@@ -45,8 +48,13 @@
events happen. No timing or other correlating information is stored - purely the number of times
things happen.
-``countableEvents`` is an object with properties representing buckets. In each bucket, there is an
-object with the following properties:
+``countableEvents`` contains a list of buckets as its properties. A bucket represents the state the browser was in when these events occurred, such as currently running an interactive tour. There are 3 types of buckets:
+
+- ``__DEFAULT__`` - No bucket, for times when the browser is not in any special state.
+- ``bucket_`` - Normal buckets, for when the browser is in a special state. The ```` in the bucket ID is the name associated with the bucket and may be further broken down into parts by the ``|`` character.
+- ``bucket_|`` - Expiring buckets, which are similar to a countdown timer. The ```` in the bucket ID describes the time interval the recorded event happened in. The intervals are ``1m`` (one minute), ``3m`` (three minutes), ``10m`` (ten minutes), and ``1h`` (one hour). After one hour, the ``__DEFAULT__`` bucket is automatically used again.
+
+Each bucket is an object with the following properties:
- ``click-builtin-item`` is an object tracking clicks on builtin customizable toolbar items, keyed
off the item IDs, with an object for each item with keys ``left``, ``middle`` and ``right`` each
@@ -86,8 +94,17 @@
``UITour``
----------
-The UI Tour has its own section in the UI Telemetry output, outside of the ``toolbars`` section.
-It has a single property ``seenPageIDs`` which tracks which UI Tour pages have been run.
+The UITour API provides ways for pages on trusted domains to safely interact with the browser UI and request it to perform actions such as opening menus and showing highlights over the browser chrome - for the purposes of interactive tours. We track some usage of this API via the ``UITour`` object in the UI Telemetry output.
+
+Each page is able to register itself with an identifier, a ``Page ID``. A list of Page IDs that have been seen over the last 8 weeks is available via ``seenPageIDs``.
+
+Page IDs are also used to identify buckets for :ref:`UITelemetry_countableEvents`, in the following circumstances:
+
+- The current tab is a tour page. This will be a normal bucket with the name ``UITour|``, where ```` is the page's registered ID. This will result in bucket IDs such as ``bucket_UITour|australis-tour``.
+- A tour tab is open but another tab is active. This will be an expiring bucket with the name ``UITour||inactive``. This will result in bucket IDs such as ``bucket_UITour|australis-tour|inactive|1m``.
+- A tour tab has recently been open but has been closed. This will be an expiring bucket with the name ``UITour||closed``. This will result in bucket IDs such as ``bucket_UITour|australis-tour|closed|10m``.
+
+
``contextmenu``
---------------
@@ -115,4 +132,3 @@
- ``custom-page-item`` is incremented when the user clicks an item that was created by the page;
- ``unknown`` is incremented when an item without an ID was clicked;
- ``other-item`` is incremented when an add-on-provided menuitem is clicked.
-
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/accounts.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/accounts.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/accounts.properties 2015-01-06 13:14:26.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/accounts.properties 2015-01-07 22:15:21.000000000 +0000
@@ -10,6 +10,12 @@
upgradeToFxA.label = Upgrade
upgradeToFxA.accessKey = U
+# LOCALIZATION NOTE (signInAfterUpgradeOnOtherDevice.description)
+# %S = Email address of user's Firefox Account
+signInAfterUpgradeOnOtherDevice.description = Sync was upgraded on another device by %S. Resume syncing?
+signInAfterUpgradeOnOtherDevice.label = Sign In
+signInAfterUpgradeOnOtherDevice.accessKey = S
+
# LOCALIZATION NOTE (needVerifiedUserShort, needVerifiedUserLong)
# %S = Email address of user's Firefox Account
needVerifiedUserShort = %S not verified
@@ -21,6 +27,13 @@
forgetMigration.label = Forget
forgetMigration.accessKey = F
+unlinkMigration.label = Unlink Sync
+unlinkMigration.accessKey = L
+
+unlinkVerificationTitle = Unlink old version of Sync?
+unlinkVerificationDescription = If you no longer want to be reminded about upgrading Sync, you can unlink your old Sync account to remove it.
+unlinkVerificationConfirm = Unlink
+
# These strings are used in a dialog we display after the user requests we resend
# a verification email.
verificationSentTitle = Verification Sent
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/devtools/projecteditor.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/devtools/projecteditor.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/devtools/projecteditor.properties 2015-01-06 13:14:26.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/devtools/projecteditor.properties 2015-01-07 22:15:22.000000000 +0000
@@ -46,6 +46,11 @@
# the directory.
projecteditor.newLabel=New…
+# LOCALIZATION NOTE (projecteditor.renameLabel):
+# This string is displayed as a menu item for renaming a file in
+# the directory.
+projecteditor.renameLabel=Rename
+
# LOCALIZATION NOTE (projecteditor.saveLabel):
# This string is displayed as a menu item for saving the current file.
projecteditor.saveLabel=Save
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/devtools/timeline.properties thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/devtools/timeline.properties
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/devtools/timeline.properties 2015-01-06 13:14:26.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/devtools/timeline.properties 2015-01-07 22:15:22.000000000 +0000
@@ -35,10 +35,11 @@
# LOCALIZATION NOTE (timeline.label.*):
# These strings are displayed in the timeline waterfall, identifying markers.
-timeline.label.styles=Styles
-timeline.label.reflow=Reflow
+# We want to use the same wording as Google Chrome
+timeline.label.styles2=Recalculate Style
+timeline.label.reflow2=Layout
timeline.label.paint=Paint
-timeline.label.javascript=Javascript
+timeline.label.javascript2=Function Call
timeline.label.domevent=DOM Event
timeline.label.consoleTime=Console
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/preferences/sync.dtd thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/preferences/sync.dtd
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/locales/en-US/chrome/browser/preferences/sync.dtd 2015-01-06 13:14:26.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/locales/en-US/chrome/browser/preferences/sync.dtd 2015-01-07 22:15:22.000000000 +0000
@@ -78,6 +78,3 @@
-
-
-
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/BrowserUITelemetry.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/BrowserUITelemetry.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/BrowserUITelemetry.jsm 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/BrowserUITelemetry.jsm 2015-01-07 22:15:22.000000000 +0000
@@ -285,9 +285,11 @@
allowPopups: false,
});
- // If there are no such windows, we're out of luck. :(
- this._firstWindowMeasurements = win ? this._getWindowMeasurements(win)
- : {};
+ Services.search.init(rv => {
+ // If there are no such windows, we're out of luck. :(
+ this._firstWindowMeasurements = win ? this._getWindowMeasurements(win, rv)
+ : {};
+ });
},
_registerWindow: function(aWindow) {
@@ -464,7 +466,7 @@
}
},
- _getWindowMeasurements: function(aWindow) {
+ _getWindowMeasurements: function(aWindow, searchResult) {
let document = aWindow.document;
let result = {};
@@ -553,6 +555,10 @@
result.visibleTabs = visibleTabs;
result.hiddenTabs = hiddenTabs;
+ if (Components.isSuccessCode(searchResult)) {
+ result.currentSearchEngine = Services.search.currentEngine;
+ }
+
return result;
},
@@ -577,6 +583,14 @@
}
},
+ countOneoffSearchEvent: function(id, type, where) {
+ this._countEvent(["search-oneoff", id, type, where]);
+ },
+
+ countSearchSettingsEvent: function(source) {
+ this._countEvent(["click-builtin-item", source, "search-settings"]);
+ },
+
_logAwesomeBarSearchResult: function (url) {
let spec = Services.search.parseSubmissionURL(url);
if (spec.engine) {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/moz.build thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/moz.build
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/moz.build 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/moz.build 2015-01-07 22:15:22.000000000 +0000
@@ -50,7 +50,6 @@
'AboutHome.jsm',
'PluginContent.jsm',
'RecentWindow.jsm',
- 'UITour.jsm',
'webrtcUI.jsm',
]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser.ini 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser.ini 2015-01-07 22:15:22.000000000 +0000
@@ -1,8 +1,6 @@
[DEFAULT]
support-files =
head.js
- image.png
- uitour.*
[browser_BrowserUITelemetry_buckets.js]
[browser_ContentSearch.js]
@@ -16,33 +14,6 @@
skip-if = e10s # Bug 666804 - Support NetworkPrioritizer in e10s
[browser_SignInToWebsite.js]
skip-if = e10s # Bug 941426 - SignIntoWebsite.jsm not e10s friendly
-[browser_UITour.js]
-skip-if = os == "linux" || e10s # Intermittent failures, bug 951965
-[browser_UITour2.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour3.js]
-skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour_availableTargets.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour_detach_tab.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour_annotation_size_attributes.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
-[browser_UITour_loop.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
-[browser_UITour_modalDialog.js]
-run-if = os == "mac" # modal dialog disabling only working on OS X
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour_observe.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
-[browser_UITour_panel_close_annotation.js]
-skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
-[browser_UITour_registerPageID.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour_sync.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
-[browser_UITour_resetProfile.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
[browser_taskbar_preview.js]
run-if = os == "win"
skip-if = e10s # Bug 666808 - AeroPeek support for e10s
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour2.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour2.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour2.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour2.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,107 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- UITourTest();
-}
-
-let tests = [
- function test_info_customize_auto_open_close(done) {
- let popup = document.getElementById("UITourTooltip");
- gContentAPI.showInfo("customize", "Customization", "Customize me please!");
- UITour.getTarget(window, "customize").then((customizeTarget) => {
- waitForPopupAtAnchor(popup, customizeTarget.node, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened before the popup anchored");
- ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
-
- // Move the info outside which should close the app menu.
- gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
- UITour.getTarget(window, "appMenu").then((target) => {
- waitForPopupAtAnchor(popup, target.node, function checkPanelIsClosed() {
- isnot(PanelUI.panel.state, "open",
- "Panel should have closed after the info moved elsewhere.");
- ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
- done();
- }, "Info should move to the appMenu button");
- });
- }, "Info panel should be anchored to the customize button");
- });
- },
- function test_info_customize_manual_open_close(done) {
- let popup = document.getElementById("UITourTooltip");
- // Manually open the app menu then show an info panel there. The menu should remain open.
- let shownPromise = promisePanelShown(window);
- gContentAPI.showMenu("appMenu");
- shownPromise.then(() => {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
- ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
- gContentAPI.showInfo("customize", "Customization", "Customize me please!");
-
- UITour.getTarget(window, "customize").then((customizeTarget) => {
- waitForPopupAtAnchor(popup, customizeTarget.node, function checkMenuIsStillOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should still be open");
- ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should still be set");
-
- // Move the info outside which shouldn't close the app menu since it was manually opened.
- gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
- UITour.getTarget(window, "appMenu").then((target) => {
- waitForPopupAtAnchor(popup, target.node, function checkMenuIsStillOpen() {
- isnot(PanelUI.panel.state, "closed",
- "Menu should remain open since UITour didn't open it in the first place");
- waitForElementToBeHidden(window.PanelUI.panel, () => {
- ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
- done();
- });
- gContentAPI.hideMenu("appMenu");
- }, "Info should move to the appMenu button");
- });
- }, "Info should be shown after showInfo() for fixed menu panel items");
- });
- }).then(null, Components.utils.reportError);
- },
- taskify(function* test_pinnedTab() {
- is(UITour.pinnedTabs.get(window), null, "Should not already have a pinned tab");
-
- yield addPinnedTabPromise();
-
- let tabInfo = UITour.pinnedTabs.get(window);
- isnot(tabInfo, null, "Should have recorded data about a pinned tab after addPinnedTab()");
- isnot(tabInfo.tab, null, "Should have added a pinned tab after addPinnedTab()");
- is(tabInfo.tab.pinned, true, "Tab should be marked as pinned");
-
- let tab = tabInfo.tab;
-
- yield removePinnedTabPromise();
- isnot(gBrowser.tabs[0], tab, "First tab should not be the pinned tab");
- tabInfo = UITour.pinnedTabs.get(window);
- is(tabInfo, null, "Should not have any data about the removed pinned tab after removePinnedTab()");
-
- yield addPinnedTabPromise();
- yield addPinnedTabPromise();
- yield addPinnedTabPromise();
- is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
- }),
- taskify(function* test_bookmarks_menu() {
- let bookmarksMenuButton = document.getElementById("bookmarks-menu-button");
-
- ise(bookmarksMenuButton.open, false, "Menu should initially be closed");
- gContentAPI.showMenu("bookmarks");
-
- yield waitForConditionPromise(() => {
- return bookmarksMenuButton.open;
- }, "Menu should be visible after showMenu()");
-
- gContentAPI.hideMenu("bookmarks");
- yield waitForConditionPromise(() => {
- return !bookmarksMenuButton.open;
- }, "Menu should be hidden after hideMenu()");
- }),
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour3.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour3.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour3.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour3.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,199 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-requestLongerTimeout(2);
-
-function test() {
- UITourTest();
-}
-
-let tests = [
- taskify(function* test_info_icon() {
- let popup = document.getElementById("UITourTooltip");
- let title = document.getElementById("UITourTooltipTitle");
- let desc = document.getElementById("UITourTooltipDescription");
- let icon = document.getElementById("UITourTooltipIcon");
- let buttons = document.getElementById("UITourTooltipButtons");
-
- // Disable the animation to prevent the mouse clicks from hitting the main
- // window during the transition instead of the buttons in the popup.
- popup.setAttribute("animate", "false");
-
- yield showInfoPromise("urlbar", "a title", "some text", "image.png");
-
- is(title.textContent, "a title", "Popup should have correct title");
- is(desc.textContent, "some text", "Popup should have correct description text");
-
- let imageURL = getRootDirectory(gTestPath) + "image.png";
- imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
- is(icon.src, imageURL, "Popup should have correct icon shown");
-
- is(buttons.hasChildNodes(), false, "Popup should have no buttons");
- }),
-
- taskify(function* test_info_buttons_1() {
- let popup = document.getElementById("UITourTooltip");
- let title = document.getElementById("UITourTooltipTitle");
- let desc = document.getElementById("UITourTooltipDescription");
- let icon = document.getElementById("UITourTooltipIcon");
-
- let buttons = gContentWindow.makeButtons();
-
- yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
-
- is(title.textContent, "another title", "Popup should have correct title");
- is(desc.textContent, "moar text", "Popup should have correct description text");
-
- let imageURL = getRootDirectory(gTestPath) + "image.png";
- imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
- is(icon.src, imageURL, "Popup should have correct icon shown");
-
- buttons = document.getElementById("UITourTooltipButtons");
- is(buttons.childElementCount, 2, "Popup should have two buttons");
-
- is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
- is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
-
- is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
- is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
-
- let promiseHidden = promisePanelElementHidden(window, popup);
- EventUtils.synthesizeMouseAtCenter(buttons.childNodes[0], {}, window);
- yield promiseHidden;
-
- ok(true, "Popup should close automatically");
-
- yield waitForCallbackResultPromise();
-
- is(gContentWindow.callbackResult, "button1", "Correct callback should have been called");
- }),
- taskify(function* test_info_buttons_2() {
- let popup = document.getElementById("UITourTooltip");
- let title = document.getElementById("UITourTooltipTitle");
- let desc = document.getElementById("UITourTooltipDescription");
- let icon = document.getElementById("UITourTooltipIcon");
-
- let buttons = gContentWindow.makeButtons();
-
- yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
-
- is(title.textContent, "another title", "Popup should have correct title");
- is(desc.textContent, "moar text", "Popup should have correct description text");
-
- let imageURL = getRootDirectory(gTestPath) + "image.png";
- imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
- is(icon.src, imageURL, "Popup should have correct icon shown");
-
- buttons = document.getElementById("UITourTooltipButtons");
- is(buttons.childElementCount, 2, "Popup should have two buttons");
-
- is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
- is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
-
- is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
- is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
-
- let promiseHidden = promisePanelElementHidden(window, popup);
- EventUtils.synthesizeMouseAtCenter(buttons.childNodes[1], {}, window);
- yield promiseHidden;
-
- ok(true, "Popup should close automatically");
-
- yield waitForCallbackResultPromise();
-
- is(gContentWindow.callbackResult, "button2", "Correct callback should have been called");
- }),
-
- taskify(function* test_info_close_button() {
- let popup = document.getElementById("UITourTooltip");
- let closeButton = document.getElementById("UITourTooltipClose");
- let infoOptions = gContentWindow.makeInfoOptions();
-
- yield showInfoPromise("urlbar", "Close me", "X marks the spot", null, null, infoOptions);
-
- EventUtils.synthesizeMouseAtCenter(closeButton, {}, window);
-
- yield waitForCallbackResultPromise();
-
- is(gContentWindow.callbackResult, "closeButton", "Close button callback called");
- }),
-
- taskify(function* test_info_target_callback() {
- let popup = document.getElementById("UITourTooltip");
- let infoOptions = gContentWindow.makeInfoOptions();
-
- yield showInfoPromise("appMenu", "I want to know when the target is clicked", "*click*", null, null, infoOptions);
-
- yield PanelUI.show();
-
- yield waitForCallbackResultPromise();
-
- is(gContentWindow.callbackResult, "target", "target callback called");
- is(gContentWindow.callbackData.target, "appMenu", "target callback was from the appMenu");
- is(gContentWindow.callbackData.type, "popupshown", "target callback was from the mousedown");
-
- // Cleanup.
- yield hideInfoPromise();
-
- popup.removeAttribute("animate");
- }),
-
- function test_getConfiguration_selectedSearchEngine(done) {
- Services.search.init(rv => {
- ok(Components.isSuccessCode(rv), "Search service initialized");
- let engine = Services.search.defaultEngine;
- gContentAPI.getConfiguration("selectedSearchEngine", (data) => {
- is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
- done();
- });
- });
- },
-
- function test_setSearchTerm(done) {
- const TERM = "UITour Search Term";
- gContentAPI.setSearchTerm(TERM);
-
- let searchbar = document.getElementById("searchbar");
- // The UITour gets to the searchbar element through a promise, so the value setting
- // only happens after a tick.
- waitForCondition(() => searchbar.value == TERM, done, "Correct term set");
- },
-
- function test_clearSearchTerm(done) {
- gContentAPI.setSearchTerm("");
-
- let searchbar = document.getElementById("searchbar");
- // The UITour gets to the searchbar element through a promise, so the value setting
- // only happens after a tick.
- waitForCondition(() => searchbar.value == "", done, "Search term cleared");
- },
-
- function test_openSearchPanel(done) {
- let searchbar = document.getElementById("searchbar");
-
- // If suggestions are enabled, the panel will attempt to use the network to connect
- // to the suggestions provider, causing the test suite to fail.
- Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
- registerCleanupFunction(() => {
- Services.prefs.clearUserPref("browser.search.suggest.enabled");
- });
-
- ok(!searchbar.textbox.open, "Popup starts as closed");
- gContentAPI.openSearchPanel(() => {
- ok(searchbar.textbox.open, "Popup was opened");
- searchbar.textbox.closePopup();
- ok(!searchbar.textbox.open, "Popup was closed");
- done();
- });
- },
-
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_annotation_size_attributes.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_annotation_size_attributes.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_annotation_size_attributes.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_annotation_size_attributes.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,51 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that width and height attributes don't get set by widget code on the highlight panel.
- */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-let highlight = document.getElementById("UITourHighlightContainer");
-let tooltip = document.getElementById("UITourTooltip");
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- UITourTest();
-}
-
-let tests = [
- function test_highlight_size_attributes(done) {
- gContentAPI.showHighlight("appMenu");
- waitForElementToBeVisible(highlight, function moveTheHighlight() {
- gContentAPI.showHighlight("urlbar");
- waitForElementToBeVisible(highlight, function checkPanelAttributes() {
- SimpleTest.executeSoon(() => {
- ise(highlight.height, "", "Highlight panel should have no explicit height set");
- ise(highlight.width, "", "Highlight panel should have no explicit width set");
- done();
- });
- }, "Highlight should be moved to the urlbar");
- }, "Highlight should be shown after showHighlight() for the appMenu");
- },
-
- function test_info_size_attributes(done) {
- gContentAPI.showInfo("appMenu", "test title", "test text");
- waitForElementToBeVisible(tooltip, function moveTheTooltip() {
- gContentAPI.showInfo("urlbar", "new title", "new text");
- waitForElementToBeVisible(tooltip, function checkPanelAttributes() {
- SimpleTest.executeSoon(() => {
- ise(tooltip.height, "", "Info panel should have no explicit height set");
- ise(tooltip.width, "", "Info panel should have no explicit width set");
- done();
- });
- }, "Tooltip should be moved to the urlbar");
- }, "Tooltip should be shown after showInfo() for the appMenu");
- },
-
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_availableTargets.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_availableTargets.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_availableTargets.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_availableTargets.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,134 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-let hasWebIDE = Services.prefs.getBoolPref("devtools.webide.widget.enabled");
-
-function test() {
- requestLongerTimeout(2);
- UITourTest();
-}
-
-function searchEngineTargets() {
- let engines = Services.search.getVisibleEngines();
- return ["searchEngine-" + engine.identifier
- for (engine of engines)
- if (engine.identifier)];
-}
-
-let tests = [
- function test_availableTargets(done) {
- gContentAPI.getConfiguration("availableTargets", (data) => {
- ok_targets(data, [
- "accountStatus",
- "addons",
- "appMenu",
- "backForward",
- "bookmarks",
- "customize",
- "help",
- "home",
- "loop",
- "devtools",
- "pinnedTab",
- "privateWindow",
- "quit",
- "search",
- "searchIcon",
- "urlbar",
- ...searchEngineTargets(),
- ...(hasWebIDE ? ["webide"] : [])
- ]);
-
- ok(UITour.availableTargetsCache.has(window),
- "Targets should now be cached");
- done();
- });
- },
-
- function test_availableTargets_changeWidgets(done) {
- CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
- ok(!UITour.availableTargetsCache.has(window),
- "Targets should be evicted from cache after widget change");
- gContentAPI.getConfiguration("availableTargets", (data) => {
- ok_targets(data, [
- "accountStatus",
- "addons",
- "appMenu",
- "backForward",
- "customize",
- "help",
- "loop",
- "devtools",
- "home",
- "pinnedTab",
- "privateWindow",
- "quit",
- "search",
- "searchIcon",
- "urlbar",
- ...searchEngineTargets(),
- ...(hasWebIDE ? ["webide"] : [])
- ]);
-
- ok(UITour.availableTargetsCache.has(window),
- "Targets should now be cached again");
- CustomizableUI.reset();
- ok(!UITour.availableTargetsCache.has(window),
- "Targets should not be cached after reset");
- done();
- });
- },
-
- function test_availableTargets_exceptionFromGetTarget(done) {
- // The query function for the "search" target will throw if it's not found.
- // Make sure the callback still fires with the other available targets.
- CustomizableUI.removeWidgetFromArea("search-container");
- gContentAPI.getConfiguration("availableTargets", (data) => {
- // Default minus "search" and "searchProvider" and "searchIcon"
- ok_targets(data, [
- "accountStatus",
- "addons",
- "appMenu",
- "backForward",
- "bookmarks",
- "customize",
- "help",
- "home",
- "loop",
- "devtools",
- "pinnedTab",
- "privateWindow",
- "quit",
- "urlbar",
- ...(hasWebIDE ? ["webide"] : [])
- ]);
-
- CustomizableUI.reset();
- done();
- });
- },
-];
-
-function ok_targets(actualData, expectedTargets) {
- // Depending on how soon after page load this is called, the selected tab icon
- // may or may not be showing the loading throbber. Check for its presence and
- // insert it into expectedTargets if it's visible.
- let selectedTabIcon =
- document.getAnonymousElementByAttribute(gBrowser.selectedTab,
- "anonid",
- "tab-icon-image");
- if (selectedTabIcon && UITour.isElementVisible(selectedTabIcon))
- expectedTargets.push("selectedTabIcon");
-
- ok(Array.isArray(actualData.targets), "data.targets should be an array");
- is(actualData.targets.sort().toString(), expectedTargets.sort().toString(),
- "Targets should be as expected");
-}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_detach_tab.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_detach_tab.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_detach_tab.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_detach_tab.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,89 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Detaching a tab to a new window shouldn't break the menu panel.
- */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-let gContentDoc;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- registerCleanupFunction(function() {
- gContentDoc = null;
- });
- UITourTest();
-}
-
-/**
- * When tab is changed we're tearing the tour down. So the UITour client has to always be aware of this
- * fact and therefore listens to visibilitychange events.
- * In particular this scenario happens for detaching the tab (ie. moving it to a new window).
- */
-let tests = [
- taskify(function* test_move_tab_to_new_window(done) {
- let onVisibilityChange = (aEvent) => {
- if (!document.hidden && window != UITour.getChromeWindow(aEvent.target)) {
- gContentAPI.showHighlight("appMenu");
- }
- };
-
- let highlight = document.getElementById("UITourHighlight");
- let windowDestroyedDeferred = Promise.defer();
- let onDOMWindowDestroyed = (aWindow) => {
- if (gContentWindow && aWindow == gContentWindow) {
- Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
- windowDestroyedDeferred.resolve();
- }
- };
-
- let browserStartupDeferred = Promise.defer();
- Services.obs.addObserver(function onBrowserDelayedStartup(aWindow) {
- Services.obs.removeObserver(onBrowserDelayedStartup, "browser-delayed-startup-finished");
- browserStartupDeferred.resolve(aWindow);
- }, "browser-delayed-startup-finished", false);
-
- // NB: we're using this rather than gContentWindow.document because the latter wouldn't
- // have an XRayWrapper, and we need to compare this to the doc we get using this method
- // later on...
- gContentDoc = gBrowser.selectedBrowser.contentDocument;
- gContentDoc.addEventListener("visibilitychange", onVisibilityChange, false);
- gContentAPI.showHighlight("appMenu");
-
- yield elementVisiblePromise(highlight);
-
- gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
-
- gContentWindow = yield browserStartupDeferred.promise;
-
- // This highlight should be shown thanks to the visibilitychange listener.
- let newWindowHighlight = gContentWindow.document.getElementById("UITourHighlight");
- yield elementVisiblePromise(newWindowHighlight);
-
- let selectedTab = gContentWindow.gBrowser.selectedTab;
- is(selectedTab.linkedBrowser && selectedTab.linkedBrowser.contentDocument, gContentDoc, "Document should be selected in new window");
- ok(UITour.originTabs && UITour.originTabs.has(gContentWindow), "Window should be known");
- ok(UITour.originTabs.get(gContentWindow).has(selectedTab), "Tab should be known");
-
- let shownPromise = promisePanelShown(gContentWindow);
- gContentAPI.showMenu("appMenu");
- yield shownPromise;
-
- isnot(gContentWindow.PanelUI.panel.state, "closed", "Panel should be open");
- ok(gContentWindow.PanelUI.contents.children.length > 0, "Panel contents should have children");
- gContentAPI.hideHighlight();
- gContentAPI.hideMenu("appMenu");
- gTestTab = null;
-
- Services.obs.addObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
- gContentWindow.close();
-
- yield windowDestroyedDeferred.promise;
- }),
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,417 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- UITourTest();
-}
-
-let tests = [
- function test_untrusted_host(done) {
- loadUITourTestPage(function() {
- let bookmarksMenu = document.getElementById("bookmarks-menu-button");
- ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
-
- gContentAPI.showMenu("bookmarks");
- ise(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");
-
- done();
- }, "http://mochi.test:8888/");
- },
- function test_testing_host(done) {
- // Add two testing origins intentionally surrounded by whitespace to be ignored.
- Services.prefs.setCharPref("browser.uitour.testingOrigins",
- "https://test1.example.com, https://test2.example.com:443 ");
-
- registerCleanupFunction(() => {
- Services.prefs.clearUserPref("browser.uitour.testingOrigins");
- });
- function callback(result) {
- ok(result, "Callback should be called on a testing origin");
- done();
- }
-
- loadUITourTestPage(function() {
- gContentAPI.getConfiguration("appinfo", callback);
- }, "https://test2.example.com/");
- },
- function test_unsecure_host(done) {
- loadUITourTestPage(function() {
- let bookmarksMenu = document.getElementById("bookmarks-menu-button");
- ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
-
- gContentAPI.showMenu("bookmarks");
- ise(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");
-
- done();
- }, "http://example.com/");
- },
- function test_unsecure_host_override(done) {
- Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
- loadUITourTestPage(function() {
- let highlight = document.getElementById("UITourHighlight");
- is_element_hidden(highlight, "Highlight should initially be hidden");
-
- gContentAPI.showHighlight("urlbar");
- waitForElementToBeVisible(highlight, done, "Highlight should be shown on a unsecure host when override pref is set");
-
- Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
- }, "http://example.com/");
- },
- function test_disabled(done) {
- Services.prefs.setBoolPref("browser.uitour.enabled", false);
-
- let bookmarksMenu = document.getElementById("bookmarks-menu-button");
- ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
-
- gContentAPI.showMenu("bookmarks");
- ise(bookmarksMenu.open, false, "Bookmark menu should not open when feature is disabled");
-
- Services.prefs.setBoolPref("browser.uitour.enabled", true);
- done();
- },
- function test_highlight(done) {
- function test_highlight_2() {
- let highlight = document.getElementById("UITourHighlight");
- gContentAPI.hideHighlight();
-
- waitForElementToBeHidden(highlight, test_highlight_3, "Highlight should be hidden after hideHighlight()");
- }
- function test_highlight_3() {
- is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");
-
- gContentAPI.showHighlight("urlbar");
- waitForElementToBeVisible(highlight, test_highlight_4, "Highlight should be shown after showHighlight()");
- }
- function test_highlight_4() {
- let highlight = document.getElementById("UITourHighlight");
- gContentAPI.showHighlight("backForward");
- waitForElementToBeVisible(highlight, done, "Highlight should be shown after showHighlight()");
- }
-
- let highlight = document.getElementById("UITourHighlight");
- is_element_hidden(highlight, "Highlight should initially be hidden");
-
- gContentAPI.showHighlight("urlbar");
- waitForElementToBeVisible(highlight, test_highlight_2, "Highlight should be shown after showHighlight()");
- },
- function test_highlight_circle(done) {
- function check_highlight_size() {
- let panel = highlight.parentElement;
- let anchor = panel.anchorNode;
- let anchorRect = anchor.getBoundingClientRect();
- info("addons target: width: " + anchorRect.width + " height: " + anchorRect.height);
- let maxDimension = Math.round(Math.max(anchorRect.width, anchorRect.height));
- let highlightRect = highlight.getBoundingClientRect();
- info("highlight: width: " + highlightRect.width + " height: " + highlightRect.height);
- is(Math.round(highlightRect.width), maxDimension, "The width of the highlight should be equal to the largest dimension of the target");
- is(Math.round(highlightRect.height), maxDimension, "The height of the highlight should be equal to the largest dimension of the target");
- is(Math.round(highlightRect.height), Math.round(highlightRect.width), "The height and width of the highlight should be the same to create a circle");
- is(highlight.style.borderRadius, "100%", "The border-radius should be 100% to create a circle");
- done();
- }
- let highlight = document.getElementById("UITourHighlight");
- is_element_hidden(highlight, "Highlight should initially be hidden");
-
- gContentAPI.showHighlight("addons");
- waitForElementToBeVisible(highlight, check_highlight_size, "Highlight should be shown after showHighlight()");
- },
- function test_highlight_customize_auto_open_close(done) {
- let highlight = document.getElementById("UITourHighlight");
- gContentAPI.showHighlight("customize");
- waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Move the highlight outside which should close the app menu.
- gContentAPI.showHighlight("appMenu");
- waitForElementToBeVisible(highlight, function checkPanelIsClosed() {
- isnot(PanelUI.panel.state, "open",
- "Panel should have closed after the highlight moved elsewhere.");
- done();
- }, "Highlight should move to the appMenu button");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
- function test_highlight_customize_manual_open_close(done) {
- let highlight = document.getElementById("UITourHighlight");
- // Manually open the app menu then show a highlight there. The menu should remain open.
- let shownPromise = promisePanelShown(window);
- gContentAPI.showMenu("appMenu");
- shownPromise.then(() => {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
- gContentAPI.showHighlight("customize");
-
- waitForElementToBeVisible(highlight, function checkPanelIsStillOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should still be open");
-
- // Move the highlight outside which shouldn't close the app menu since it was manually opened.
- gContentAPI.showHighlight("appMenu");
- waitForElementToBeVisible(highlight, function () {
- isnot(PanelUI.panel.state, "closed",
- "Panel should remain open since UITour didn't open it in the first place");
- gContentAPI.hideMenu("appMenu");
- done();
- }, "Highlight should move to the appMenu button");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- }).then(null, Components.utils.reportError);
- },
- function test_highlight_effect(done) {
- function waitForHighlightWithEffect(highlightEl, effect, next, error) {
- return waitForCondition(() => highlightEl.getAttribute("active") == effect,
- next,
- error);
- }
- function checkDefaultEffect() {
- is(highlight.getAttribute("active"), "none", "The default should be no effect");
-
- gContentAPI.showHighlight("urlbar", "none");
- waitForHighlightWithEffect(highlight, "none", checkZoomEffect, "There should be no effect");
- }
- function checkZoomEffect() {
- gContentAPI.showHighlight("urlbar", "zoom");
- waitForHighlightWithEffect(highlight, "zoom", () => {
- let style = window.getComputedStyle(highlight);
- is(style.animationName, "uitour-zoom", "The animation-name should be uitour-zoom");
- checkSameEffectOnDifferentTarget();
- }, "There should be a zoom effect");
- }
- function checkSameEffectOnDifferentTarget() {
- gContentAPI.showHighlight("appMenu", "wobble");
- waitForHighlightWithEffect(highlight, "wobble", () => {
- highlight.addEventListener("animationstart", function onAnimationStart(aEvent) {
- highlight.removeEventListener("animationstart", onAnimationStart);
- ok(true, "Animation occurred again even though the effect was the same");
- checkRandomEffect();
- });
- gContentAPI.showHighlight("backForward", "wobble");
- }, "There should be a wobble effect");
- }
- function checkRandomEffect() {
- function waitForActiveHighlight(highlightEl, next, error) {
- return waitForCondition(() => highlightEl.hasAttribute("active"),
- next,
- error);
- }
-
- gContentAPI.hideHighlight();
- gContentAPI.showHighlight("urlbar", "random");
- waitForActiveHighlight(highlight, () => {
- ok(highlight.hasAttribute("active"), "The highlight should be active");
- isnot(highlight.getAttribute("active"), "none", "A random effect other than none should have been chosen");
- isnot(highlight.getAttribute("active"), "random", "The random effect shouldn't be 'random'");
- isnot(UITour.highlightEffects.indexOf(highlight.getAttribute("active")), -1, "Check that a supported effect was randomly chosen");
- done();
- }, "There should be an active highlight with a random effect");
- }
-
- let highlight = document.getElementById("UITourHighlight");
- is_element_hidden(highlight, "Highlight should initially be hidden");
-
- gContentAPI.showHighlight("urlbar");
- waitForElementToBeVisible(highlight, checkDefaultEffect, "Highlight should be shown after showHighlight()");
- },
- function test_highlight_search_engine(done) {
- let highlight = document.getElementById("UITourHighlight");
- gContentAPI.showHighlight("urlbar");
- waitForElementToBeVisible(highlight, () => {
-
- let searchbar = document.getElementById("searchbar");
- if (searchbar.getAttribute("oneoffui")) {
- done();
- return; // The oneoffui removes the menu that's being tested here.
- }
-
- gContentAPI.showMenu("searchEngines", function() {
- isnot(searchbar, null, "Should have found searchbar");
- let searchPopup = document.getAnonymousElementByAttribute(searchbar,
- "anonid",
- "searchbar-popup");
- isnot(searchPopup, null, "Should have found search popup");
-
- function getEngineNode(identifier) {
- let engineNode = null;
- for (let node of searchPopup.children) {
- if (node.engine.identifier == identifier) {
- engineNode = node;
- break;
- }
- }
- isnot(engineNode, null, "Should have found search engine node in popup");
- return engineNode;
- }
- let googleEngineNode = getEngineNode("google");
- let bingEngineNode = getEngineNode("bing");
-
- gContentAPI.showHighlight("searchEngine-google");
- waitForCondition(() => googleEngineNode.getAttribute("_moz-menuactive") == "true", function() {
- is_element_hidden(highlight, "Highlight panel should be hidden by highlighting search engine");
-
- gContentAPI.showHighlight("searchEngine-bing");
- waitForCondition(() => bingEngineNode.getAttribute("_moz-menuactive") == "true", function() {
- isnot(googleEngineNode.getAttribute("_moz-menuactive"), "true", "Previous engine should no longer be highlighted");
-
- gContentAPI.hideHighlight();
- waitForCondition(() => bingEngineNode.getAttribute("_moz-menuactive") != "true", function() {
- gContentAPI.hideMenu("searchEngines");
- waitForCondition(() => searchPopup.state == "closed", function() {
- done();
- }, "Search dropdown should close");
- }, "Menu item should get attribute removed");
- }, "Menu item should get attribute to make it look active");
- });
- });
- });
- },
- function test_highlight_effect_unsupported(done) {
- function checkUnsupportedEffect() {
- is(highlight.getAttribute("active"), "none", "No effect should be used when an unsupported effect is requested");
- done();
- }
-
- let highlight = document.getElementById("UITourHighlight");
- is_element_hidden(highlight, "Highlight should initially be hidden");
-
- gContentAPI.showHighlight("urlbar", "__UNSUPPORTED__");
- waitForElementToBeVisible(highlight, checkUnsupportedEffect, "Highlight should be shown after showHighlight()");
- },
- function test_info_1(done) {
- let popup = document.getElementById("UITourTooltip");
- let title = document.getElementById("UITourTooltipTitle");
- let desc = document.getElementById("UITourTooltipDescription");
- let icon = document.getElementById("UITourTooltipIcon");
- let buttons = document.getElementById("UITourTooltipButtons");
-
- popup.addEventListener("popupshown", function onPopupShown() {
- popup.removeEventListener("popupshown", onPopupShown);
- is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
- is(title.textContent, "test title", "Popup should have correct title");
- is(desc.textContent, "test text", "Popup should have correct description text");
- is(icon.src, "", "Popup should have no icon");
- is(buttons.hasChildNodes(), false, "Popup should have no buttons");
-
- popup.addEventListener("popuphidden", function onPopupHidden() {
- popup.removeEventListener("popuphidden", onPopupHidden);
-
- popup.addEventListener("popupshown", function onPopupShown() {
- popup.removeEventListener("popupshown", onPopupShown);
- done();
- });
-
- gContentAPI.showInfo("urlbar", "test title", "test text");
-
- });
- gContentAPI.hideInfo();
- });
-
- gContentAPI.showInfo("urlbar", "test title", "test text");
- },
- taskify(function* test_info_2() {
- let popup = document.getElementById("UITourTooltip");
- let title = document.getElementById("UITourTooltipTitle");
- let desc = document.getElementById("UITourTooltipDescription");
- let icon = document.getElementById("UITourTooltipIcon");
- let buttons = document.getElementById("UITourTooltipButtons");
-
- yield showInfoPromise("urlbar", "urlbar title", "urlbar text");
-
- is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
- is(title.textContent, "urlbar title", "Popup should have correct title");
- is(desc.textContent, "urlbar text", "Popup should have correct description text");
- is(icon.src, "", "Popup should have no icon");
- is(buttons.hasChildNodes(), false, "Popup should have no buttons");
-
- yield showInfoPromise("search", "search title", "search text");
-
- is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
- is(title.textContent, "search title", "Popup should have correct title");
- is(desc.textContent, "search text", "Popup should have correct description text");
- }),
- function test_getConfigurationVersion(done) {
- function callback(result) {
- let props = ["defaultUpdateChannel", "version"];
- for (let property of props) {
- ok(typeof(result[property]) !== undefined, "Check " + property + " isn't undefined.");
- is(result[property], Services.appinfo[property], "Should have the same " + property + " property.");
- }
- done();
- }
-
- gContentAPI.getConfiguration("appinfo", callback);
- },
- function test_addToolbarButton(done) {
- let placement = CustomizableUI.getPlacementOfWidget("panic-button");
- is(placement, null, "default UI has panic button in the palette");
-
- gContentAPI.getConfiguration("availableTargets", (data) => {
- let available = (data.targets.indexOf("forget") != -1);
- ok(!available, "Forget button should not be available by default");
-
- gContentAPI.addNavBarWidget("forget", () => {
- info("addNavBarWidget callback successfully called");
-
- let placement = CustomizableUI.getPlacementOfWidget("panic-button");
- is(placement.area, CustomizableUI.AREA_NAVBAR);
-
- gContentAPI.getConfiguration("availableTargets", (data) => {
- let available = (data.targets.indexOf("forget") != -1);
- ok(available, "Forget button should now be available");
-
- // Cleanup
- CustomizableUI.removeWidgetFromArea("panic-button");
- done();
- });
- });
- });
- },
- function test_select_search_engine(done) {
- Services.search.init(rv => {
- if (!Components.isSuccessCode(rv)) {
- ok(false, "search service init failed: " + rv);
- done();
- return;
- }
- let defaultEngine = Services.search.defaultEngine;
- gContentAPI.getConfiguration("availableTargets", data => {
- let searchEngines = data.targets.filter(t => t.startsWith("searchEngine-"));
- let someOtherEngineID = searchEngines.filter(t => t != "searchEngine-" + defaultEngine.identifier)[0];
- someOtherEngineID = someOtherEngineID.replace(/^searchEngine-/, "");
-
- let observe = function (subject, topic, verb) {
- info("browser-search-engine-modified: " + verb);
- if (verb == "engine-current") {
- is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
- done();
- }
- };
- Services.obs.addObserver(observe, "browser-search-engine-modified", false);
- registerCleanupFunction(() => {
- // Clean up
- Services.obs.removeObserver(observe, "browser-search-engine-modified");
- Services.search.defaultEngine = defaultEngine;
- });
-
- gContentAPI.setDefaultSearchEngine(someOtherEngineID);
- });
- });
- },
- function test_treatment_tag(done) {
- gContentAPI.setTreatmentTag("foobar", "baz");
- gContentAPI.getTreatmentTag("foobar", (data) => {
- is(data.value, "baz", "set and retrieved treatmentTag");
- done();
- });
- },
-
- // Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
- taskify(function* cleanupMenus() {
- let shownPromise = promisePanelShown(window);
- gContentAPI.showMenu("appMenu");
- yield shownPromise;
- }),
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_loop.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_loop.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_loop.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_loop.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,286 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-let loopButton;
-let loopPanel = document.getElementById("loop-notification-panel");
-
-Components.utils.import("resource:///modules/UITour.jsm");
-const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
-
-function test() {
- UITourTest();
-}
-
-let tests = [
- taskify(function* test_menu_show_hide() {
- ise(loopButton.open, false, "Menu should initially be closed");
- gContentAPI.showMenu("loop");
-
- yield waitForConditionPromise(() => {
- return loopButton.open;
- }, "Menu should be visible after showMenu()");
-
- ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
- ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
- is(loopPanel.state, "open", "The panel should be open");
- ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
-
- gContentAPI.hideMenu("loop");
- yield waitForConditionPromise(() => {
- return !loopButton.open;
- }, "Menu should be hidden after hideMenu()");
-
- checkLoopPanelIsHidden();
- }),
- // Test the menu was cleaned up in teardown.
- taskify(function* setup_menu_cleanup() {
- gContentAPI.showMenu("loop");
-
- yield waitForConditionPromise(() => {
- return loopButton.open;
- }, "Menu should be visible after showMenu()");
-
- // Leave it open so it gets torn down and we can test below that teardown was succesful.
- }),
- taskify(function* test_menu_cleanup() {
- // Test that the open menu from above was torn down fully.
- checkLoopPanelIsHidden();
- }),
- function test_availableTargets(done) {
- gContentAPI.showMenu("loop");
- gContentAPI.getConfiguration("availableTargets", (data) => {
- for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
- isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
- }
- done();
- });
- },
- function test_hideMenuHidesAnnotations(done) {
- let infoPanel = document.getElementById("UITourTooltip");
- let highlightPanel = document.getElementById("UITourHighlightContainer");
-
- gContentAPI.showMenu("loop", function menuCallback() {
- gContentAPI.showHighlight("loop-roomList");
- gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
- UITour.getTarget(window, "loop-newRoom").then((target) => {
- waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
- isnot(loopPanel.state, "closed", "Loop panel should still be open");
- ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
- is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
- is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
-
- info("Close the loop menu and make sure the annotations inside disappear");
- let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
- promisePanelElementHidden(window, highlightPanel)];
- gContentAPI.hideMenu("loop");
- yield Promise.all(hiddenPromises);
- isnot(infoPanel.state, "open", "Info panel should have automatically hid");
- isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
- done();
- }), "Info panel should be anchored to the new room button");
- });
- });
- },
- function test_notifyLoopChatWindowOpenedClosed(done) {
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
- gContentAPI.observe((event, params) => {
- ok(false, "No more notifications should have arrived");
- });
- });
- done();
- });
- document.querySelector("#pinnedchats > chatbox").close();
- });
- LoopRooms.open("fakeTourRoom");
- },
- function test_notifyLoopRoomURLCopied(done) {
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
-
- let chat = document.querySelector("#pinnedchats > chatbox");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:RoomURLCopied", "Check Loop:RoomURLCopied notification");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
- });
- chat.close();
- done();
- });
- chat.content.contentDocument.querySelector(".btn-copy").click();
- });
- });
- setupFakeRoom();
- LoopRooms.open("fakeTourRoom");
- },
- function test_notifyLoopRoomURLEmailed(done) {
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
-
- let chat = document.querySelector("#pinnedchats > chatbox");
- let composeEmailCalled = false;
-
- gContentAPI.observe((event, params) => {
- is(event, "Loop:RoomURLEmailed", "Check Loop:RoomURLEmailed notification");
- ok(composeEmailCalled, "mozLoop.composeEmail should be called");
- gContentAPI.observe((event, params) => {
- is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
- });
- chat.close();
- done();
- });
-
- let chatWin = chat.content.contentWindow;
- let oldComposeEmail = chatWin.navigator.wrappedJSObject.mozLoop.composeEmail;
- chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = function(recipient, subject, body) {
- ok(recipient, "composeEmail should be invoked with at least a recipient value");
- composeEmailCalled = true;
- chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = oldComposeEmail;
- };
- chatWin.document.querySelector(".btn-email").click();
- });
- });
- LoopRooms.open("fakeTourRoom");
- },
- taskify(function* test_arrow_panel_position() {
- ise(loopButton.open, false, "Menu should initially be closed");
- let popup = document.getElementById("UITourTooltip");
-
- yield showMenuPromise("loop");
-
- let currentTarget = "loop-newRoom";
- yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
- is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
-
- currentTarget = "loop-roomList";
- yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
- is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
-
- currentTarget = "loop-signInUpLink";
- yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be underneath");
- is(popup.popupBoxObject.alignmentPosition, "after_end", "Check " + currentTarget + " position");
- }),
- taskify(function* test_setConfiguration() {
- is(Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin"), false, "pref should be false but exist");
- gContentAPI.setConfiguration("Loop:ResumeTourOnFirstJoin", true);
-
- yield waitForConditionPromise(() => {
- return Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin");
- }, "Pref should change to true via setConfiguration");
-
- Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
- }),
- taskify(function* test_resumeViaMenuPanel_roomClosedTabOpen() {
- Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
-
- // Create a fake room and then add a fake non-owner participant
- let roomsMap = setupFakeRoom();
- roomsMap.get("fakeTourRoom").participants = [{
- owner: false,
- }];
-
- // Set the tour URL to be the current page with a different query param
- let gettingStartedURL = gTestTab.linkedBrowser.currentURI.resolve("?gettingstarted=1");
- Services.prefs.setCharPref("loop.gettingStarted.url", gettingStartedURL);
-
- let observationPromise = new Promise((resolve) => {
- gContentAPI.observe((event, params) => {
- is(event, "Loop:IncomingConversation", "Page should have been notified about incoming conversation");
- ise(params.conversationOpen, false, "conversationOpen should be false");
- is(gBrowser.selectedTab, gTestTab, "The same tab should be selected");
- resolve();
- });
- });
-
- // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
- yield showMenuPromise("loop");
-
- yield observationPromise;
- Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
- }),
- taskify(function* test_resumeViaMenuPanel_roomClosedTabClosed() {
- Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
-
- // Create a fake room and then add a fake non-owner participant
- let roomsMap = setupFakeRoom();
- roomsMap.get("fakeTourRoom").participants = [{
- owner: false,
- }];
-
- // Set the tour URL to a page that's not open yet
- Services.prefs.setCharPref("loop.gettingStarted.url", gBrowser.currentURI.prePath);
-
- let newTabPromise = waitForConditionPromise(() => {
- return gBrowser.currentURI.path.contains("incomingConversation=waiting");
- }, "New tab with incomingConversation=waiting should have opened");
-
- // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
- yield showMenuPromise("loop");
-
- yield newTabPromise;
-
- yield gBrowser.removeCurrentTab();
- Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
- }),
-];
-
-// End tests
-
-function checkLoopPanelIsHidden() {
- ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
- ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
- isnot(loopPanel.state, "open", "The panel shouldn't be open");
- is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
-}
-
-function setupFakeRoom() {
- let room = {};
- for (let prop of ["roomToken", "roomName", "roomOwner", "roomUrl", "participants"])
- room[prop] = "fakeTourRoom";
- let roomsMap = new Map([
- [room.roomToken, room]
- ]);
- LoopRooms.stubCache(roomsMap);
- return roomsMap;
-}
-
-if (Services.prefs.getBoolPref("loop.enabled")) {
- loopButton = window.LoopUI.toolbarButton.node;
- // The targets to highlight only appear after getting started is launched.
- Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
-
- registerCleanupFunction(() => {
- Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
- Services.prefs.clearUserPref("loop.gettingStarted.seen");
- Services.prefs.clearUserPref("loop.gettingStarted.url");
-
- // Copied from browser/components/loop/test/mochitest/head.js
- // Remove the iframe after each test. This also avoids mochitest complaining
- // about leaks on shutdown as we intentionally hold the iframe open for the
- // life of the application.
- let frameId = loopButton.getAttribute("notificationFrameId");
- let frame = document.getElementById(frameId);
- if (frame) {
- frame.remove();
- }
-
- // Remove the stubbed rooms
- LoopRooms.stubCache(null);
- });
-} else {
- ok(true, "Loop is disabled so skip the UITour Loop tests");
- tests = [];
-}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_modalDialog.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_modalDialog.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_modalDialog.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_modalDialog.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,106 +0,0 @@
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-let handleDialog;
-
-// Modified from toolkit/components/passwordmgr/test/prompt_common.js
-var didDialog;
-
-var timer; // keep in outer scope so it's not GC'd before firing
-function startCallbackTimer() {
- didDialog = false;
-
- // Delay before the callback twiddles the prompt.
- const dialogDelay = 10;
-
- // Use a timer to invoke a callback to twiddle the authentication dialog
- timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- timer.init(observer, dialogDelay, Ci.nsITimer.TYPE_ONE_SHOT);
-}
-
-
-var observer = SpecialPowers.wrapCallbackObject({
- QueryInterface : function (iid) {
- const interfaces = [Ci.nsIObserver,
- Ci.nsISupports, Ci.nsISupportsWeakReference];
-
- if (!interfaces.some( function(v) { return iid.equals(v) } ))
- throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
- return this;
- },
-
- observe : function (subject, topic, data) {
- var doc = getDialogDoc();
- if (doc)
- handleDialog(doc);
- else
- startCallbackTimer(); // try again in a bit
- }
-});
-
-function getDialogDoc() {
- // Find the which contains notifyWindow, by looking
- // through all the open windows and all the in each.
- var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
- getService(Ci.nsIWindowMediator);
- //var enumerator = wm.getEnumerator("navigator:browser");
- var enumerator = wm.getXULWindowEnumerator(null);
-
- while (enumerator.hasMoreElements()) {
- var win = enumerator.getNext();
- var windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
-
- var containedDocShells = windowDocShell.getDocShellEnumerator(
- Ci.nsIDocShellTreeItem.typeChrome,
- Ci.nsIDocShell.ENUMERATE_FORWARDS);
- while (containedDocShells.hasMoreElements()) {
- // Get the corresponding document for this docshell
- var childDocShell = containedDocShells.getNext();
- // We don't want it if it's not done loading.
- if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
- continue;
- var childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
- .contentViewer
- .DOMDocument;
-
- //ok(true, "Got window: " + childDoc.location.href);
- if (childDoc.location.href == "chrome://global/content/commonDialog.xul")
- return childDoc;
- }
- }
-
- return null;
-}
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- UITourTest();
-}
-
-
-let tests = [
- taskify(function* test_modal_dialog_while_opening_tooltip(done) {
- let panelShown;
- let popup;
-
- handleDialog = (doc) => {
- popup = document.getElementById("UITourTooltip");
- gContentAPI.showInfo("appMenu", "test title", "test text");
- doc.defaultView.setTimeout(function() {
- is(popup.state, "closed", "Popup shouldn't be shown while dialog is up");
- panelShown = promisePanelElementShown(window, popup);
- let dialog = doc.getElementById("commonDialog");
- dialog.acceptDialog();
- }, 1000);
- };
- startCallbackTimer();
- executeSoon(() => alert("test"));
- yield waitForConditionPromise(() => panelShown, "Timed out waiting for panel promise to be assigned", 100);
- yield panelShown;
-
- yield hideInfoPromise();
- })
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_observe.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_observe.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_observe.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_observe.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,51 +0,0 @@
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- requestLongerTimeout(2);
- UITourTest();
-}
-
-let tests = [
- function test_no_params(done) {
- function listener(event, params) {
- is(event, "test-event-1", "Correct event name");
- is(params, null, "No param object");
- gContentAPI.observe(null);
- done();
- }
-
- gContentAPI.observe(listener, () => {
- UITour.notify("test-event-1");
- });
- },
- function test_param_string(done) {
- function listener(event, params) {
- is(event, "test-event-2", "Correct event name");
- is(params, "a param", "Correct param string");
- gContentAPI.observe(null);
- done();
- }
-
- gContentAPI.observe(listener, () => {
- UITour.notify("test-event-2", "a param");
- });
- },
- function test_param_object(done) {
- function listener(event, params) {
- is(event, "test-event-3", "Correct event name");
- is(JSON.stringify(params), JSON.stringify({key: "something"}), "Correct param object");
- gContentAPI.observe(null);
- done();
- }
-
- gContentAPI.observe(listener, () => {
- UITour.notify("test-event-3", {key: "something"});
- });
- },
-];
\ No newline at end of file
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_panel_close_annotation.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_panel_close_annotation.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_panel_close_annotation.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_panel_close_annotation.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,155 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests that annotations disappear when their target is hidden.
- */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-let highlight = document.getElementById("UITourHighlight");
-let tooltip = document.getElementById("UITourTooltip");
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- registerCleanupFunction(() => {
- // Close the find bar in case it's open in the remaining tab
- gBrowser.getFindBar(gBrowser.selectedTab).close();
- });
- UITourTest();
-}
-
-let tests = [
- function test_highlight_move_outside_panel(done) {
- gContentAPI.showInfo("urlbar", "test title", "test text");
- gContentAPI.showHighlight("customize");
- waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Move the highlight outside which should close the app menu.
- gContentAPI.showHighlight("appMenu");
- waitForPopupAtAnchor(highlight.parentElement, document.getElementById("PanelUI-button"), () => {
- isnot(PanelUI.panel.state, "open",
- "Panel should have closed after the highlight moved elsewhere.");
- ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
- done();
- }, "Highlight should move to the appMenu button and still be visible");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
-
- function test_highlight_panel_hideMenu(done) {
- gContentAPI.showHighlight("customize");
- gContentAPI.showInfo("search", "test title", "test text");
- waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Close the app menu and make sure the highlight also disappeared.
- gContentAPI.hideMenu("appMenu");
- waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
- isnot(PanelUI.panel.state, "open",
- "Panel still should have closed");
- ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
- done();
- }, "Highlight should have disappeared when panel closed");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
-
- function test_highlight_panel_click_find(done) {
- gContentAPI.showHighlight("help");
- gContentAPI.showInfo("searchIcon", "test title", "test text");
- waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Click the find button which should close the panel.
- let findButton = document.getElementById("find-button");
- EventUtils.synthesizeMouseAtCenter(findButton, {});
- waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
- isnot(PanelUI.panel.state, "open",
- "Panel should have closed when the find bar opened");
- ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
- done();
- }, "Highlight should have disappeared when panel closed");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
-
- function test_highlight_info_panel_click_find(done) {
- gContentAPI.showHighlight("help");
- gContentAPI.showInfo("customize", "customize me!", "awesome!");
- waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Click the find button which should close the panel.
- let findButton = document.getElementById("find-button");
- EventUtils.synthesizeMouseAtCenter(findButton, {});
- waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
- isnot(PanelUI.panel.state, "open",
- "Panel should have closed when the find bar opened");
- waitForElementToBeHidden(tooltip, function checkTooltipIsClosed() {
- isnot(tooltip.state, "open", "The info panel should have closed too");
- done();
- }, "Tooltip should hide with the menu");
- }, "Highlight should have disappeared when panel closed");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
-
- function test_highlight_panel_open_subview(done) {
- gContentAPI.showHighlight("customize");
- gContentAPI.showInfo("backForward", "test title", "test text");
- waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Click the help button which should open the subview in the panel menu.
- let helpButton = document.getElementById("PanelUI-help");
- EventUtils.synthesizeMouseAtCenter(helpButton, {});
- waitForElementToBeHidden(highlight, function highlightHidden() {
- is(PanelUI.panel.state, "open",
- "Panel should have stayed open when the subview opened");
- ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
- PanelUI.hide();
- done();
- }, "Highlight should have disappeared when the subview opened");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
-
- function test_info_panel_open_subview(done) {
- gContentAPI.showHighlight("urlbar");
- gContentAPI.showInfo("customize", "customize me!", "Open a subview");
- waitForElementToBeVisible(tooltip, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Click the help button which should open the subview in the panel menu.
- let helpButton = document.getElementById("PanelUI-help");
- EventUtils.synthesizeMouseAtCenter(helpButton, {});
- waitForElementToBeHidden(tooltip, function tooltipHidden() {
- is(PanelUI.panel.state, "open",
- "Panel should have stayed open when the subview opened");
- is(highlight.parentElement.state, "open", "The highlight should have remained open");
- PanelUI.hide();
- done();
- }, "Tooltip should have disappeared when the subview opened");
- }, "Highlight should be shown after showHighlight() for fixed panel items");
- },
-
- function test_info_move_outside_panel(done) {
- gContentAPI.showInfo("addons", "test title", "test text");
- gContentAPI.showHighlight("urlbar");
- let addonsButton = document.getElementById("add-ons-button");
- waitForPopupAtAnchor(tooltip, addonsButton, function checkPanelIsOpen() {
- isnot(PanelUI.panel.state, "closed", "Panel should have opened");
-
- // Move the info panel outside which should close the app menu.
- gContentAPI.showInfo("appMenu", "Cool menu button", "It's three lines");
- waitForPopupAtAnchor(tooltip, document.getElementById("PanelUI-button"), () => {
- isnot(PanelUI.panel.state, "open",
- "Menu should have closed after the highlight moved elsewhere.");
- is(highlight.parentElement.state, "open", "The highlight should have remained visible");
- done();
- }, "Tooltip should move to the appMenu button and still be visible");
- }, "Tooltip should be shown after showInfo() for a panel item");
- },
-
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_registerPageID.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_registerPageID.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_registerPageID.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_registerPageID.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,112 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-Components.utils.import("resource://gre/modules/UITelemetry.jsm");
-Components.utils.import("resource:///modules/BrowserUITelemetry.jsm");
-
-function test() {
- UITelemetry._enabled = true;
-
- registerCleanupFunction(function() {
- Services.prefs.clearUserPref("browser.uitour.seenPageIDs");
- resetSeenPageIDsLazyGetter();
- UITelemetry._enabled = undefined;
- BrowserUITelemetry.setBucket(null);
- delete window.UITelemetry;
- delete window.BrowserUITelemetry;
- });
- UITourTest();
-}
-
-function resetSeenPageIDsLazyGetter() {
- delete UITour.seenPageIDs;
- // This should be kept in sync with how UITour.init() sets this.
- Object.defineProperty(UITour, "seenPageIDs", {
- get: UITour.restoreSeenPageIDs.bind(UITour),
- configurable: true,
- });
-}
-
-function checkExpectedSeenPageIDs(expected) {
- is(UITour.seenPageIDs.size, expected.length, "Should be " + expected.length + " total seen page IDs");
-
- for (let id of expected)
- ok(UITour.seenPageIDs.has(id), "Should have seen '" + id + "' page ID");
-
- let prefData = Services.prefs.getCharPref("browser.uitour.seenPageIDs");
- prefData = new Map(JSON.parse(prefData));
-
- is(prefData.size, expected.length, "Should be " + expected.length + " total seen page IDs persisted");
-
- for (let id of expected)
- ok(prefData.has(id), "Should have seen '" + id + "' page ID persisted");
-}
-
-let tests = [
- function test_seenPageIDs_restore(done) {
- info("Setting up seenPageIDs to be restored from pref");
- let data = JSON.stringify([
- ["savedID1", { lastSeen: Date.now() }],
- ["savedID2", { lastSeen: Date.now() }],
- // 9 weeks ago, should auto expire.
- ["savedID3", { lastSeen: Date.now() - 9 * 7 * 24 * 60 * 60 * 1000 }],
- ]);
- Services.prefs.setCharPref("browser.uitour.seenPageIDs",
- data);
-
- resetSeenPageIDsLazyGetter();
- checkExpectedSeenPageIDs(["savedID1", "savedID2"]);
-
- done();
- },
- taskify(function* test_seenPageIDs_set_1() {
- gContentAPI.registerPageID("testpage1");
-
- yield waitForConditionPromise(() => UITour.seenPageIDs.size == 3, "Waiting for page to be registered.");
-
- checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1"]);
-
- const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
- const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
-
- let bucket = PREFIX + "UITour" + SEP + "testpage1";
- is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
-
- gBrowser.selectedTab = gBrowser.addTab("about:blank");
- bucket = PREFIX + "UITour" + SEP + "testpage1" + SEP + "inactive" + SEP + "1m";
- is(BrowserUITelemetry.currentBucket, bucket,
- "After switching tabs, bucket should be expiring");
-
- gBrowser.removeTab(gBrowser.selectedTab);
- gBrowser.selectedTab = gTestTab;
- BrowserUITelemetry.setBucket(null);
- }),
- taskify(function* test_seenPageIDs_set_2() {
- gContentAPI.registerPageID("testpage2");
-
- yield waitForConditionPromise(() => UITour.seenPageIDs.size == 4, "Waiting for page to be registered.");
-
- checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1", "testpage2"]);
-
- const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
- const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
-
- let bucket = PREFIX + "UITour" + SEP + "testpage2";
- is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
-
- gBrowser.removeTab(gTestTab);
- gTestTab = null;
- bucket = PREFIX + "UITour" + SEP + "testpage2" + SEP + "closed" + SEP + "1m";
- is(BrowserUITelemetry.currentBucket, bucket,
- "After closing tab, bucket should be expiring");
-
- BrowserUITelemetry.setBucket(null);
- }),
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_resetProfile.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_resetProfile.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_resetProfile.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_resetProfile.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,39 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- UITourTest();
-}
-
-let tests = [
- // Test that a reset profile dialog appears when "resetFirefox" event is triggered
- function test_resetFirefox(done) {
- let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
- getService(Ci.nsIWindowWatcher);
- winWatcher.registerNotification(function onOpen(subj, topic, data) {
- if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
- subj.addEventListener("load", function onLoad() {
- subj.removeEventListener("load", onLoad);
- if (subj.document.documentURI ==
- "chrome://global/content/resetProfile.xul") {
- winWatcher.unregisterNotification(onOpen);
- ok(true, "Observed search manager window open");
- is(subj.opener, window,
- "Reset Firefox event opened a reset profile window.");
- subj.close();
- done();
- }
- });
- }
- });
- gContentAPI.resetFirefox();
- },
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_sync.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_sync.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/browser_UITour_sync.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/browser_UITour_sync.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,67 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let gTestTab;
-let gContentAPI;
-let gContentWindow;
-
-Components.utils.import("resource:///modules/UITour.jsm");
-
-function test() {
- registerCleanupFunction(function() {
- Services.prefs.clearUserPref("services.sync.username");
- });
- UITourTest();
-}
-
-let tests = [
- function test_checkSyncSetup_disabled(done) {
- function callback(result) {
- is(result.setup, false, "Sync shouldn't be setup by default");
- done();
- }
-
- gContentAPI.getConfiguration("sync", callback);
- },
-
- function test_checkSyncSetup_enabled(done) {
- function callback(result) {
- is(result.setup, true, "Sync should be setup");
- done();
- }
-
- Services.prefs.setCharPref("services.sync.username", "uitour@tests.mozilla.org");
- gContentAPI.getConfiguration("sync", callback);
- },
-
- // The showFirefoxAccounts API is sync related, so we test that here too...
- function test_firefoxAccounts(done) {
- // This test will load about:accounts, and we don't want that to hit the
- // network.
- Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri",
- "https://example.com/");
-
- loadUITourTestPage(function(contentWindow) {
- let tabBrowser = gBrowser.selectedBrowser;
- // This command will replace the current tab - so add a load event
- // handler which will fire when that happens.
- tabBrowser.addEventListener("load", function onload(evt) {
- tabBrowser.removeEventListener("load", onload, true);
-
- ise(tabBrowser.contentDocument.location.href,
- "about:accounts?action=signup&entrypoint=uitour",
- "about:accounts should have replaced the tab");
-
- // the iframe in about:accounts will still be loading, so we stop
- // that before resetting the pref.
- tabBrowser.contentDocument.location.href = "about:blank";
- Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
- done();
- }, true);
-
- gContentAPI.showFirefoxAccounts();
- });
- },
-];
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/head.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/head.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/head.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/head.js 2015-01-07 22:15:22.000000000 +0000
@@ -1,10 +1,4 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
Cu.import("resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UITour",
- "resource:///modules/UITour.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
const SINGLE_TRY_TIMEOUT = 100;
const NUMBER_OF_TRIES = 30;
@@ -37,240 +31,3 @@
ok(false, reason + (reason.stack ? "\n" + e.stack : ""));
});
}
-
-/**
- * Wrapper to partially transition tests to Task.
- */
-function taskify(fun) {
- return (done) => {
- return Task.spawn(fun).then(done, (reason) => {
- ok(false, reason);
- done();
- });
- }
-}
-
-function is_hidden(element) {
- var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
- if (style.display == "none")
- return true;
- if (style.visibility != "visible")
- return true;
- if (style.display == "-moz-popup")
- return ["hiding","closed"].indexOf(element.state) != -1;
-
- // Hiding a parent element will hide all its children
- if (element.parentNode != element.ownerDocument)
- return is_hidden(element.parentNode);
-
- return false;
-}
-
-function is_visible(element) {
- var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
- if (style.display == "none")
- return false;
- if (style.visibility != "visible")
- return false;
- if (style.display == "-moz-popup" && element.state != "open")
- return false;
-
- // Hiding a parent element will hide all its children
- if (element.parentNode != element.ownerDocument)
- return is_visible(element.parentNode);
-
- return true;
-}
-
-function is_element_visible(element, msg) {
- isnot(element, null, "Element should not be null, when checking visibility");
- ok(is_visible(element), msg);
-}
-
-function waitForElementToBeVisible(element, nextTest, msg) {
- waitForCondition(() => is_visible(element),
- () => {
- ok(true, msg);
- nextTest();
- },
- "Timeout waiting for visibility: " + msg);
-}
-
-function waitForElementToBeHidden(element, nextTest, msg) {
- waitForCondition(() => is_hidden(element),
- () => {
- ok(true, msg);
- nextTest();
- },
- "Timeout waiting for invisibility: " + msg);
-}
-
-function elementVisiblePromise(element, msg) {
- return waitForConditionPromise(() => is_visible(element), "Timeout waiting for visibility: " + msg);
-}
-
-function elementHiddenPromise(element, msg) {
- return waitForConditionPromise(() => is_hidden(element), "Timeout waiting for invisibility: " + msg);
-}
-
-function waitForPopupAtAnchor(popup, anchorNode, nextTest, msg) {
- waitForCondition(() => is_visible(popup) && popup.popupBoxObject.anchorNode == anchorNode,
- () => {
- ok(true, msg);
- is_element_visible(popup, "Popup should be visible");
- nextTest();
- },
- "Timeout waiting for popup at anchor: " + msg);
-}
-
-function hideInfoPromise(...args) {
- let popup = document.getElementById("UITourTooltip");
- gContentAPI.hideInfo.apply(gContentAPI, args);
- return promisePanelElementHidden(window, popup);
-}
-
-function showInfoPromise(...args) {
- let popup = document.getElementById("UITourTooltip");
- gContentAPI.showInfo.apply(gContentAPI, args);
- return promisePanelElementShown(window, popup);
-}
-
-function showMenuPromise(name) {
- return new Promise(resolve => {
- gContentAPI.showMenu(name, () => resolve());
- });
-}
-
-function waitForCallbackResultPromise() {
- return waitForConditionPromise(() => {
- return gContentWindow.callbackResult;
- }, "callback should be called");
-}
-
-function addPinnedTabPromise() {
- gContentAPI.addPinnedTab();
- return waitForConditionPromise(() => {
- let tabInfo = UITour.pinnedTabs.get(window);
- if (!tabInfo) {
- return false;
- }
- return tabInfo.tab.pinned;
- });
-}
-
-function removePinnedTabPromise() {
- gContentAPI.removePinnedTab();
- return waitForConditionPromise(() => {
- let tabInfo = UITour.pinnedTabs.get(window);
- return tabInfo == null;
- });
-}
-
-function promisePanelShown(win) {
- let panelEl = win.PanelUI.panel;
- return promisePanelElementShown(win, panelEl);
-}
-
-function promisePanelElementEvent(win, aPanel, aEvent) {
- let deferred = Promise.defer();
- let timeoutId = win.setTimeout(() => {
- deferred.reject("Panel did not show within 5 seconds.");
- }, 5000);
- aPanel.addEventListener(aEvent, function onPanelEvent(e) {
- aPanel.removeEventListener(aEvent, onPanelEvent);
- win.clearTimeout(timeoutId);
- deferred.resolve();
- });
- return deferred.promise;
-}
-
-function promisePanelElementShown(win, aPanel) {
- return promisePanelElementEvent(win, aPanel, "popupshown");
-}
-
-function promisePanelElementHidden(win, aPanel) {
- return promisePanelElementEvent(win, aPanel, "popuphidden");
-}
-
-function is_element_hidden(element, msg) {
- isnot(element, null, "Element should not be null, when checking visibility");
- ok(is_hidden(element), msg);
-}
-
-function loadUITourTestPage(callback, host = "https://example.com/") {
- if (gTestTab)
- gBrowser.removeTab(gTestTab);
-
- let url = getRootDirectory(gTestPath) + "uitour.html";
- url = url.replace("chrome://mochitests/content/", host);
-
- gTestTab = gBrowser.addTab(url);
- gBrowser.selectedTab = gTestTab;
-
- gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
- gTestTab.linkedBrowser.removeEventListener("load", onLoad, true);
-
- gContentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
- gContentAPI = gContentWindow.Mozilla.UITour;
-
- waitForFocus(callback, gContentWindow);
- }, true);
-}
-
-function UITourTest() {
- Services.prefs.setBoolPref("browser.uitour.enabled", true);
- let testUri = Services.io.newURI("http://example.com", null, null);
- Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
-
- waitForExplicitFinish();
-
- registerCleanupFunction(function() {
- delete window.UITour;
- delete window.UITourMetricsProvider;
- delete window.gContentWindow;
- delete window.gContentAPI;
- if (gTestTab)
- gBrowser.removeTab(gTestTab);
- delete window.gTestTab;
- Services.prefs.clearUserPref("browser.uitour.enabled", true);
- Services.perms.remove("example.com", "uitour");
- });
-
- function done() {
- executeSoon(() => {
- if (gTestTab)
- gBrowser.removeTab(gTestTab);
- gTestTab = null;
-
- let highlight = document.getElementById("UITourHighlightContainer");
- is_element_hidden(highlight, "Highlight should be closed/hidden after UITour tab is closed");
-
- let tooltip = document.getElementById("UITourTooltip");
- is_element_hidden(tooltip, "Tooltip should be closed/hidden after UITour tab is closed");
-
- ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up");
- ok(!PanelUI.panel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
- isnot(PanelUI.panel.state, "open", "The panel shouldn't be open");
- is(document.getElementById("PanelUI-menu-button").hasAttribute("open"), false, "Menu button should know that the menu is closed");
-
- is(UITour.pinnedTabs.get(window), null, "Any pinned tab should be closed after UITour tab is closed");
-
- executeSoon(nextTest);
- });
- }
-
- function nextTest() {
- if (tests.length == 0) {
- finish();
- return;
- }
- let test = tests.shift();
- info("Starting " + test.name);
- waitForFocus(function() {
- loadUITourTestPage(function() {
- test(done);
- });
- });
- }
- nextTest();
-}
Binary files /tmp/ZALrx69OUO/thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/image.png and /tmp/O2YioUdL7r/thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/image.png differ
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/uitour.html thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/uitour.html
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/uitour.html 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/uitour.html 1970-01-01 00:00:00.000000000 +0000
@@ -1,39 +0,0 @@
-
-
-
-
- UITour test
-
-
-
-
-
UITour tests
-
Because Firefox is...
-
Never gonna let you down
-
Never gonna give you up
-
-
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/uitour.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/uitour.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/test/uitour.js 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/test/uitour.js 1970-01-01 00:00:00.000000000 +0000
@@ -1,280 +0,0 @@
-/* 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/. */
-
-// create namespace
-if (typeof Mozilla == 'undefined') {
- var Mozilla = {};
-}
-
-;(function($) {
- 'use strict';
-
- // create namespace
- if (typeof Mozilla.UITour == 'undefined') {
- Mozilla.UITour = {};
- }
-
- var themeIntervalId = null;
- function _stopCyclingThemes() {
- if (themeIntervalId) {
- clearInterval(themeIntervalId);
- themeIntervalId = null;
- }
- }
-
- function _sendEvent(action, data) {
- var event = new CustomEvent('mozUITour', {
- bubbles: true,
- detail: {
- action: action,
- data: data || {}
- }
- });
-
- document.dispatchEvent(event);
- }
-
- function _generateCallbackID() {
- return Math.random().toString(36).replace(/[^a-z]+/g, '');
- }
-
- function _waitForCallback(callback) {
- var id = _generateCallbackID();
-
- function listener(event) {
- if (typeof event.detail != 'object')
- return;
- if (event.detail.callbackID != id)
- return;
-
- document.removeEventListener('mozUITourResponse', listener);
- callback(event.detail.data);
- }
- document.addEventListener('mozUITourResponse', listener);
-
- return id;
- }
-
- var notificationListener = null;
- function _notificationListener(event) {
- if (typeof event.detail != 'object')
- return;
- if (typeof notificationListener != 'function')
- return;
-
- notificationListener(event.detail.event, event.detail.params);
- }
-
- Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
-
- Mozilla.UITour.CONFIGNAME_SYNC = 'sync';
- Mozilla.UITour.CONFIGNAME_AVAILABLETARGETS = 'availableTargets';
-
- Mozilla.UITour.ping = function(callback) {
- var data = {};
- if (callback) {
- data.callbackID = _waitForCallback(callback);
- }
- _sendEvent('ping', data);
- };
-
- Mozilla.UITour.observe = function(listener, callback) {
- notificationListener = listener;
-
- if (listener) {
- document.addEventListener('mozUITourNotification',
- _notificationListener);
- Mozilla.UITour.ping(callback);
- } else {
- document.removeEventListener('mozUITourNotification',
- _notificationListener);
- }
- };
-
- Mozilla.UITour.registerPageID = function(pageID) {
- _sendEvent('registerPageID', {
- pageID: pageID
- });
- };
-
- Mozilla.UITour.showHighlight = function(target, effect) {
- _sendEvent('showHighlight', {
- target: target,
- effect: effect
- });
- };
-
- Mozilla.UITour.hideHighlight = function() {
- _sendEvent('hideHighlight');
- };
-
- Mozilla.UITour.showInfo = function(target, title, text, icon, buttons, options) {
- var buttonData = [];
- if (Array.isArray(buttons)) {
- for (var i = 0; i < buttons.length; i++) {
- buttonData.push({
- label: buttons[i].label,
- icon: buttons[i].icon,
- style: buttons[i].style,
- callbackID: _waitForCallback(buttons[i].callback)
- });
- }
- }
-
- var closeButtonCallbackID, targetCallbackID;
- if (options && options.closeButtonCallback)
- closeButtonCallbackID = _waitForCallback(options.closeButtonCallback);
- if (options && options.targetCallback)
- targetCallbackID = _waitForCallback(options.targetCallback);
-
- _sendEvent('showInfo', {
- target: target,
- title: title,
- text: text,
- icon: icon,
- buttons: buttonData,
- closeButtonCallbackID: closeButtonCallbackID,
- targetCallbackID: targetCallbackID
- });
- };
-
- Mozilla.UITour.hideInfo = function() {
- _sendEvent('hideInfo');
- };
-
- Mozilla.UITour.previewTheme = function(theme) {
- _stopCyclingThemes();
-
- _sendEvent('previewTheme', {
- theme: JSON.stringify(theme)
- });
- };
-
- Mozilla.UITour.resetTheme = function() {
- _stopCyclingThemes();
-
- _sendEvent('resetTheme');
- };
-
- Mozilla.UITour.cycleThemes = function(themes, delay, callback) {
- _stopCyclingThemes();
-
- if (!delay) {
- delay = Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY;
- }
-
- function nextTheme() {
- var theme = themes.shift();
- themes.push(theme);
-
- _sendEvent('previewTheme', {
- theme: JSON.stringify(theme),
- state: true
- });
-
- callback(theme);
- }
-
- themeIntervalId = setInterval(nextTheme, delay);
- nextTheme();
- };
-
- Mozilla.UITour.addPinnedTab = function() {
- _sendEvent('addPinnedTab');
- };
-
- Mozilla.UITour.removePinnedTab = function() {
- _sendEvent('removePinnedTab');
- };
-
- Mozilla.UITour.showMenu = function(name, callback) {
- var showCallbackID;
- if (callback)
- showCallbackID = _waitForCallback(callback);
-
- _sendEvent('showMenu', {
- name: name,
- showCallbackID: showCallbackID,
- });
- };
-
- Mozilla.UITour.hideMenu = function(name) {
- _sendEvent('hideMenu', {
- name: name
- });
- };
-
- Mozilla.UITour.startUrlbarCapture = function(text, url) {
- _sendEvent('startUrlbarCapture', {
- text: text,
- url: url
- });
- };
-
- Mozilla.UITour.endUrlbarCapture = function() {
- _sendEvent('endUrlbarCapture');
- };
-
- Mozilla.UITour.getConfiguration = function(configName, callback) {
- _sendEvent('getConfiguration', {
- callbackID: _waitForCallback(callback),
- configuration: configName,
- });
- };
-
- Mozilla.UITour.setConfiguration = function(configName, configValue) {
- _sendEvent('setConfiguration', {
- configuration: configName,
- value: configValue,
- });
- };
-
- Mozilla.UITour.showFirefoxAccounts = function() {
- _sendEvent('showFirefoxAccounts');
- };
-
- Mozilla.UITour.resetFirefox = function() {
- _sendEvent('resetFirefox');
- };
-
- Mozilla.UITour.addNavBarWidget= function(name, callback) {
- _sendEvent('addNavBarWidget', {
- name: name,
- callbackID: _waitForCallback(callback),
- });
- };
-
- Mozilla.UITour.setDefaultSearchEngine = function(identifier) {
- _sendEvent('setDefaultSearchEngine', {
- identifier: identifier,
- });
- };
-
- Mozilla.UITour.setTreatmentTag = function(name, value) {
- _sendEvent('setTreatmentTag', {
- name: name,
- value: value
- });
- };
-
- Mozilla.UITour.getTreatmentTag = function(name, callback) {
- _sendEvent('getTreatmentTag', {
- name: name,
- callbackID: _waitForCallback(callback)
- });
- };
-
- Mozilla.UITour.setSearchTerm = function(term) {
- _sendEvent('setSearchTerm', {
- term: term
- });
- };
-
- Mozilla.UITour.openSearchPanel = function(callback) {
- _sendEvent('openSearchPanel', {
- callbackID: _waitForCallback(callback)
- });
- };
-
-})();
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/UITour.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/UITour.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/modules/UITour.jsm 2015-01-06 13:14:27.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/modules/UITour.jsm 1970-01-01 00:00:00.000000000 +0000
@@ -1,1811 +0,0 @@
-// 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";
-
-this.EXPORTED_SYMBOLS = ["UITour", "UITourMetricsProvider"];
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
- "resource://gre/modules/LightweightThemeManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
- "resource://gre/modules/ResetProfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
- "resource:///modules/CustomizableUI.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
- "resource://gre/modules/UITelemetry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
- "resource:///modules/BrowserUITelemetry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
- "resource://gre/modules/Metrics.jsm");
-
-// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
-const PREF_LOG_LEVEL = "browser.uitour.loglevel";
-const PREF_SEENPAGEIDS = "browser.uitour.seenPageIDs";
-const MAX_BUTTONS = 4;
-
-const BUCKET_NAME = "UITour";
-const BUCKET_TIMESTEPS = [
- 1 * 60 * 1000, // Until 1 minute after tab is closed/inactive.
- 3 * 60 * 1000, // Until 3 minutes after tab is closed/inactive.
- 10 * 60 * 1000, // Until 10 minutes after tab is closed/inactive.
- 60 * 60 * 1000, // Until 1 hour after tab is closed/inactive.
-];
-
-// Time after which seen Page IDs expire.
-const SEENPAGEID_EXPIRY = 8 * 7 * 24 * 60 * 60 * 1000; // 8 weeks.
-
-// Prefix for any target matching a search engine.
-const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
-
-// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
-XPCOMUtils.defineLazyGetter(this, "log", () => {
- let ConsoleAPI = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).ConsoleAPI;
- let consoleOptions = {
- // toLowerCase is because the loglevel values use title case to be compatible with Log.jsm.
- maxLogLevel: Services.prefs.getCharPref(PREF_LOG_LEVEL).toLowerCase(),
- prefix: "UITour",
- };
- return new ConsoleAPI(consoleOptions);
-});
-
-this.UITour = {
- url: null,
- seenPageIDs: null,
- pageIDSourceTabs: new WeakMap(),
- pageIDSourceWindows: new WeakMap(),
- /* Map from browser windows to a set of tabs in which a tour is open */
- originTabs: new WeakMap(),
- /* Map from browser windows to a set of pinned tabs opened by (a) tour(s) */
- pinnedTabs: new WeakMap(),
- urlbarCapture: new WeakMap(),
- appMenuOpenForAnnotation: new Set(),
- availableTargetsCache: new WeakMap(),
-
- _detachingTab: false,
- _annotationPanelMutationObservers: new WeakMap(),
- _queuedEvents: [],
- _pendingDoc: null,
-
- highlightEffects: ["random", "wobble", "zoom", "color"],
- targets: new Map([
- ["accountStatus", {
- query: (aDocument) => {
- let statusButton = aDocument.getElementById("PanelUI-fxa-status");
- return aDocument.getAnonymousElementByAttribute(statusButton,
- "class",
- "toolbarbutton-icon");
- },
- widgetName: "PanelUI-fxa-status",
- }],
- ["addons", {query: "#add-ons-button"}],
- ["appMenu", {
- addTargetListener: (aDocument, aCallback) => {
- let panelPopup = aDocument.getElementById("PanelUI-popup");
- panelPopup.addEventListener("popupshown", aCallback);
- },
- query: "#PanelUI-button",
- removeTargetListener: (aDocument, aCallback) => {
- let panelPopup = aDocument.getElementById("PanelUI-popup");
- panelPopup.removeEventListener("popupshown", aCallback);
- },
- }],
- ["backForward", {
- query: "#back-button",
- widgetName: "urlbar-container",
- }],
- ["bookmarks", {query: "#bookmarks-menu-button"}],
- ["customize", {
- query: (aDocument) => {
- let customizeButton = aDocument.getElementById("PanelUI-customize");
- return aDocument.getAnonymousElementByAttribute(customizeButton,
- "class",
- "toolbarbutton-icon");
- },
- widgetName: "PanelUI-customize",
- }],
- ["devtools", {query: "#developer-button"}],
- ["help", {query: "#PanelUI-help"}],
- ["home", {query: "#home-button"}],
- ["forget", {
- query: "#panic-button",
- widgetName: "panic-button",
- allowAdd: true,
- }],
- ["loop", {query: "#loop-button"}],
- ["loop-newRoom", {
- infoPanelPosition: "leftcenter topright",
- query: (aDocument) => {
- let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
- if (!loopBrowser) {
- return null;
- }
- // Use the parentElement full-width container of the button so our arrow
- // doesn't overlap the panel contents much.
- return loopBrowser.contentDocument.querySelector(".new-room-button").parentElement;
- },
- }],
- ["loop-roomList", {
- infoPanelPosition: "leftcenter topright",
- query: (aDocument) => {
- let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
- if (!loopBrowser) {
- return null;
- }
- return loopBrowser.contentDocument.querySelector(".room-list");
- },
- }],
- ["loop-selectedRoomButtons", {
- infoPanelOffsetY: -20,
- infoPanelPosition: "start_after",
- query: (aDocument) => {
- let chatbox = aDocument.querySelector("chatbox[src^='about\:loopconversation'][selected]");
-
- // Check that the real target actually exists
- if (!chatbox || !chatbox.contentDocument ||
- !chatbox.contentDocument.querySelector(".call-action-group")) {
- return null;
- }
-
- // But anchor on the in the chatbox so the panel doesn't jump to undefined
- // positions when the copy/email buttons disappear e.g. when the feedback form opens or
- // somebody else joins the room.
- return chatbox.content;
- },
- }],
- ["loop-signInUpLink", {
- query: (aDocument) => {
- let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
- if (!loopBrowser) {
- return null;
- }
- return loopBrowser.contentDocument.querySelector(".signin-link");
- },
- }],
- ["privateWindow", {query: "#privatebrowsing-button"}],
- ["quit", {query: "#PanelUI-quit"}],
- ["search", {
- infoPanelOffsetX: 18,
- infoPanelPosition: "after_start",
- query: "#searchbar",
- widgetName: "search-container",
- }],
- ["searchProvider", {
- query: (aDocument) => {
- let searchbar = aDocument.getElementById("searchbar");
- if (searchbar.hasAttribute("oneoffui")) {
- return null;
- }
- return aDocument.getAnonymousElementByAttribute(searchbar,
- "anonid",
- "searchbar-engine-button");
- },
- widgetName: "search-container",
- }],
- ["searchIcon", {
- query: (aDocument) => {
- let searchbar = aDocument.getElementById("searchbar");
- if (!searchbar.hasAttribute("oneoffui")) {
- return null;
- }
- return aDocument.getAnonymousElementByAttribute(searchbar,
- "anonid",
- "searchbar-search-button");
- },
- widgetName: "search-container",
- }],
- ["searchPrefsLink", {
- query: (aDocument) => {
- let element = null;
- let searchbar = aDocument.getElementById("searchbar");
- if (searchbar.hasAttribute("oneoffui")) {
- let popup = aDocument.getElementById("PopupSearchAutoComplete");
- if (popup.state != "open")
- return null;
- element = aDocument.getAnonymousElementByAttribute(popup,
- "anonid",
- "search-settings");
- } else {
- element = aDocument.getAnonymousElementByAttribute(searchbar,
- "anonid",
- "open-engine-manager");
- }
- if (!element || !UITour.isElementVisible(element)) {
- return null;
- }
- return element;
- },
- }],
- ["selectedTabIcon", {
- query: (aDocument) => {
- let selectedtab = aDocument.defaultView.gBrowser.selectedTab;
- let element = aDocument.getAnonymousElementByAttribute(selectedtab,
- "anonid",
- "tab-icon-image");
- if (!element || !UITour.isElementVisible(element)) {
- return null;
- }
- return element;
- },
- }],
- ["urlbar", {
- query: "#urlbar",
- widgetName: "urlbar-container",
- }],
- ["webide", {query: "#webide-button"}],
- ]),
-
- init: function() {
- log.debug("Initializing UITour");
- // Lazy getter is initialized here so it can be replicated any time
- // in a test.
- delete this.seenPageIDs;
- Object.defineProperty(this, "seenPageIDs", {
- get: this.restoreSeenPageIDs.bind(this),
- configurable: true,
- });
-
- delete this.url;
- XPCOMUtils.defineLazyGetter(this, "url", function () {
- return Services.urlFormatter.formatURLPref("browser.uitour.url");
- });
-
- // Clear the availableTargetsCache on widget changes.
- let listenerMethods = [
- "onWidgetAdded",
- "onWidgetMoved",
- "onWidgetRemoved",
- "onWidgetReset",
- "onAreaReset",
- ];
- CustomizableUI.addListener(listenerMethods.reduce((listener, method) => {
- listener[method] = () => this.availableTargetsCache.clear();
- return listener;
- }, {}));
- },
-
- restoreSeenPageIDs: function() {
- delete this.seenPageIDs;
-
- if (UITelemetry.enabled) {
- let dateThreshold = Date.now() - SEENPAGEID_EXPIRY;
-
- try {
- let data = Services.prefs.getCharPref(PREF_SEENPAGEIDS);
- data = new Map(JSON.parse(data));
-
- for (let [pageID, details] of data) {
-
- if (typeof pageID != "string" ||
- typeof details != "object" ||
- typeof details.lastSeen != "number" ||
- details.lastSeen < dateThreshold) {
-
- data.delete(pageID);
- }
- }
-
- this.seenPageIDs = data;
- } catch (e) {}
- }
-
- if (!this.seenPageIDs)
- this.seenPageIDs = new Map();
-
- this.persistSeenIDs();
-
- return this.seenPageIDs;
- },
-
- addSeenPageID: function(aPageID) {
- if (!UITelemetry.enabled)
- return;
-
- this.seenPageIDs.set(aPageID, {
- lastSeen: Date.now(),
- });
-
- this.persistSeenIDs();
- },
-
- persistSeenIDs: function() {
- if (this.seenPageIDs.size === 0) {
- Services.prefs.clearUserPref(PREF_SEENPAGEIDS);
- return;
- }
-
- Services.prefs.setCharPref(PREF_SEENPAGEIDS,
- JSON.stringify([...this.seenPageIDs]));
- },
-
- onPageEvent: function(aMessage, aEvent) {
- let contentDocument = null;
- let browser = aMessage.target;
- let window = browser.ownerDocument.defaultView;
- let tab = window.gBrowser.getTabForBrowser(browser);
- let messageManager = browser.messageManager;
-
- log.debug("onPageEvent:", aEvent.detail);
-
- if (typeof aEvent.detail != "object") {
- log.warn("Malformed event - detail not an object");
- return false;
- }
-
- let action = aEvent.detail.action;
- if (typeof action != "string" || !action) {
- log.warn("Action not defined");
- return false;
- }
-
- let data = aEvent.detail.data;
- if (typeof data != "object") {
- log.warn("Malformed event - data not an object");
- return false;
- }
-
- // Do this before bailing if there's no tab, so later we can pick up the pieces:
- window.gBrowser.tabContainer.addEventListener("TabSelect", this);
-
- if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
- contentDocument = browser.contentWindow.document;
- if (!tab) {
- // This should only happen while detaching a tab:
- if (this._detachingTab) {
- log.debug("Got event while detatching a tab");
- this._queuedEvents.push(aEvent);
- this._pendingDoc = Cu.getWeakReference(contentDocument);
- return;
- }
- log.error("Discarding tabless UITour event (" + action + ") while not detaching a tab." +
- "This shouldn't happen!");
- return;
- }
- }
-
- switch (action) {
- case "registerPageID": {
- // This is only relevant if Telemtry is enabled.
- if (!UITelemetry.enabled) {
- log.debug("registerPageID: Telemery disabled, not doing anything");
- break;
- }
-
- // We don't want to allow BrowserUITelemetry.BUCKET_SEPARATOR in the
- // pageID, as it could make parsing the telemetry bucket name difficult.
- if (typeof data.pageID != "string" ||
- data.pageID.contains(BrowserUITelemetry.BUCKET_SEPARATOR)) {
- log.warn("registerPageID: Invalid page ID specified");
- break;
- }
-
- this.addSeenPageID(data.pageID);
-
- // Store tabs and windows separately so we don't need to loop over all
- // tabs when a window is closed.
- this.pageIDSourceTabs.set(tab, data.pageID);
- this.pageIDSourceWindows.set(window, data.pageID);
-
- this.setTelemetryBucket(data.pageID);
-
- break;
- }
-
- case "showHighlight": {
- let targetPromise = this.getTarget(window, data.target);
- targetPromise.then(target => {
- if (!target.node) {
- log.error("UITour: Target could not be resolved: " + data.target);
- return;
- }
- let effect = undefined;
- if (this.highlightEffects.indexOf(data.effect) !== -1) {
- effect = data.effect;
- }
- this.showHighlight(window, target, effect);
- }).catch(log.error);
- break;
- }
-
- case "hideHighlight": {
- this.hideHighlight(window);
- break;
- }
-
- case "showInfo": {
- let targetPromise = this.getTarget(window, data.target, true);
- targetPromise.then(target => {
- if (!target.node) {
- log.error("UITour: Target could not be resolved: " + data.target);
- return;
- }
-
- let iconURL = null;
- if (typeof data.icon == "string")
- iconURL = this.resolveURL(browser, data.icon);
-
- let buttons = [];
- if (Array.isArray(data.buttons) && data.buttons.length > 0) {
- for (let buttonData of data.buttons) {
- if (typeof buttonData == "object" &&
- typeof buttonData.label == "string" &&
- typeof buttonData.callbackID == "string") {
- let button = {
- label: buttonData.label,
- callbackID: buttonData.callbackID,
- };
-
- if (typeof buttonData.icon == "string")
- button.iconURL = this.resolveURL(browser, buttonData.icon);
-
- if (typeof buttonData.style == "string")
- button.style = buttonData.style;
-
- buttons.push(button);
-
- if (buttons.length == MAX_BUTTONS) {
- log.warn("showInfo: Reached limit of allowed number of buttons");
- break;
- }
- }
- }
- }
-
- let infoOptions = {};
-
- if (typeof data.closeButtonCallbackID == "string")
- infoOptions.closeButtonCallbackID = data.closeButtonCallbackID;
- if (typeof data.targetCallbackID == "string")
- infoOptions.targetCallbackID = data.targetCallbackID;
-
- this.showInfo(window, messageManager, target, data.title, data.text, iconURL, buttons, infoOptions);
- }).catch(log.error);
- break;
- }
-
- case "hideInfo": {
- this.hideInfo(window);
- break;
- }
-
- case "previewTheme": {
- this.previewTheme(data.theme);
- break;
- }
-
- case "resetTheme": {
- this.resetTheme();
- break;
- }
-
- case "addPinnedTab": {
- this.ensurePinnedTab(window, true);
- break;
- }
-
- case "removePinnedTab": {
- this.removePinnedTab(window);
- break;
- }
-
- case "showMenu": {
- this.showMenu(window, data.name, () => {
- if (typeof data.showCallbackID == "string")
- this.sendPageCallback(messageManager, data.showCallbackID);
- });
- break;
- }
-
- case "hideMenu": {
- this.hideMenu(window, data.name);
- break;
- }
-
- case "startUrlbarCapture": {
- if (typeof data.text != "string" || !data.text ||
- typeof data.url != "string" || !data.url) {
- log.warn("startUrlbarCapture: Text or URL not specified");
- return false;
- }
-
- let uri = null;
- try {
- uri = Services.io.newURI(data.url, null, null);
- } catch (e) {
- log.warn("startUrlbarCapture: Malformed URL specified");
- return false;
- }
-
- let secman = Services.scriptSecurityManager;
- let principal = contentDocument.nodePrincipal;
- let flags = secman.DISALLOW_INHERIT_PRINCIPAL;
- try {
- secman.checkLoadURIWithPrincipal(principal, uri, flags);
- } catch (e) {
- log.warn("startUrlbarCapture: Orginating page doesn't have permission to open specified URL");
- return false;
- }
-
- this.startUrlbarCapture(window, data.text, data.url);
- break;
- }
-
- case "endUrlbarCapture": {
- this.endUrlbarCapture(window);
- break;
- }
-
- case "getConfiguration": {
- if (typeof data.configuration != "string") {
- log.warn("getConfiguration: No configuration option specified");
- return false;
- }
-
- this.getConfiguration(messageManager, window, data.configuration, data.callbackID);
- break;
- }
-
- case "setConfiguration": {
- if (typeof data.configuration != "string") {
- log.warn("setConfiguration: No configuration option specified");
- return false;
- }
-
- this.setConfiguration(data.configuration, data.value);
- break;
- }
-
- case "showFirefoxAccounts": {
- // 'signup' is the only action that makes sense currently, so we don't
- // accept arbitrary actions just to be safe...
- // We want to replace the current tab.
- contentDocument.location.href = "about:accounts?action=signup&entrypoint=uitour";
- break;
- }
-
- case "resetFirefox": {
- // Open a reset profile dialog window.
- ResetProfile.openConfirmationDialog(window);
- break;
- }
-
- case "addNavBarWidget": {
- // Add a widget to the toolbar
- let targetPromise = this.getTarget(window, data.name);
- targetPromise.then(target => {
- this.addNavBarWidget(target, messageManager, data.callbackID);
- }).catch(log.error);
- break;
- }
-
- case "setDefaultSearchEngine": {
- let enginePromise = this.selectSearchEngine(data.identifier);
- enginePromise.catch(Cu.reportError);
- break;
- }
-
- case "setTreatmentTag": {
- let name = data.name;
- let value = data.value;
- let string = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
- string.data = value;
- Services.prefs.setComplexValue("browser.uitour.treatment." + name,
- Ci.nsISupportsString, string);
- UITourHealthReport.recordTreatmentTag(name, value);
- break;
- }
-
- case "getTreatmentTag": {
- let name = data.name;
- let value;
- try {
- value = Services.prefs.getComplexValue("browser.uitour.treatment." + name,
- Ci.nsISupportsString).data;
- } catch (ex) {}
- this.sendPageCallback(messageManager, data.callbackID, { value: value });
- break;
- }
-
- case "setSearchTerm": {
- let targetPromise = this.getTarget(window, "search");
- targetPromise.then(target => {
- let searchbar = target.node;
- searchbar.value = data.term;
- searchbar.inputChanged();
- }).then(null, Cu.reportError);
- break;
- }
-
- case "openSearchPanel": {
- let targetPromise = this.getTarget(window, "search");
- targetPromise.then(target => {
- let searchbar = target.node;
-
- if (searchbar.textbox.open) {
- this.sendPageCallback(messageManager, data.callbackID);
- } else {
- let onPopupShown = () => {
- searchbar.textbox.popup.removeEventListener("popupshown", onPopupShown);
- this.sendPageCallback(messageManager, data.callbackID);
- };
-
- searchbar.textbox.popup.addEventListener("popupshown", onPopupShown);
- searchbar.openSuggestionsPanel();
- }
- }).then(null, Cu.reportError);
- break;
- }
-
- case "ping": {
- if (typeof data.callbackID == "string")
- this.sendPageCallback(messageManager, data.callbackID);
- break;
- }
- }
-
- if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
- if (!this.originTabs.has(window)) {
- this.originTabs.set(window, new Set());
- }
-
- this.originTabs.get(window).add(tab);
- tab.addEventListener("TabClose", this);
- tab.addEventListener("TabBecomingWindow", this);
- window.addEventListener("SSWindowClosing", this);
- }
-
- return true;
- },
-
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "pagehide": {
- let window = this.getChromeWindow(aEvent.target);
- this.teardownTour(window);
- break;
- }
-
- case "TabBecomingWindow":
- this._detachingTab = true;
- // Fall through
- case "TabClose": {
- let tab = aEvent.target;
- if (this.pageIDSourceTabs.has(tab)) {
- let pageID = this.pageIDSourceTabs.get(tab);
-
- // Delete this from the window cache, so if the window is closed we
- // don't expire this page ID twice.
- let window = tab.ownerDocument.defaultView;
- if (this.pageIDSourceWindows.get(window) == pageID)
- this.pageIDSourceWindows.delete(window);
-
- this.setExpiringTelemetryBucket(pageID, "closed");
- }
-
- let window = tab.ownerDocument.defaultView;
- this.teardownTour(window);
- break;
- }
-
- case "TabSelect": {
- if (aEvent.detail && aEvent.detail.previousTab) {
- let previousTab = aEvent.detail.previousTab;
-
- if (this.pageIDSourceTabs.has(previousTab)) {
- let pageID = this.pageIDSourceTabs.get(previousTab);
- this.setExpiringTelemetryBucket(pageID, "inactive");
- }
- }
-
- let window = aEvent.target.ownerDocument.defaultView;
- let selectedTab = window.gBrowser.selectedTab;
- let pinnedTab = this.pinnedTabs.get(window);
- if (pinnedTab && pinnedTab.tab == selectedTab)
- break;
- let originTabs = this.originTabs.get(window);
- if (originTabs && originTabs.has(selectedTab))
- break;
-
- let pendingDoc;
- if (this._detachingTab && this._pendingDoc && (pendingDoc = this._pendingDoc.get())) {
- if (selectedTab.linkedBrowser.contentDocument == pendingDoc) {
- if (!this.originTabs.get(window)) {
- this.originTabs.set(window, new Set());
- }
- this.originTabs.get(window).add(selectedTab);
- this.pendingDoc = null;
- this._detachingTab = false;
- while (this._queuedEvents.length) {
- try {
- this.onPageEvent(this._queuedEvents.shift());
- } catch (ex) {
- log.error(ex);
- }
- }
- break;
- }
- }
-
- this.teardownTour(window);
- break;
- }
-
- case "SSWindowClosing": {
- let window = aEvent.target;
- if (this.pageIDSourceWindows.has(window)) {
- let pageID = this.pageIDSourceWindows.get(window);
- this.setExpiringTelemetryBucket(pageID, "closed");
- }
-
- this.teardownTour(window, true);
- break;
- }
-
- case "input": {
- if (aEvent.target.id == "urlbar") {
- let window = aEvent.target.ownerDocument.defaultView;
- this.handleUrlbarInput(window);
- }
- break;
- }
- }
- },
-
- setTelemetryBucket: function(aPageID) {
- let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID;
- BrowserUITelemetry.setBucket(bucket);
- },
-
- setExpiringTelemetryBucket: function(aPageID, aType) {
- let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID +
- BrowserUITelemetry.BUCKET_SEPARATOR + aType;
-
- BrowserUITelemetry.setExpiringBucket(bucket,
- BUCKET_TIMESTEPS);
- },
-
- // This is registered with UITelemetry by BrowserUITelemetry, so that UITour
- // can remain lazy-loaded on-demand.
- getTelemetry: function() {
- return {
- seenPageIDs: [...this.seenPageIDs.keys()],
- };
- },
-
- teardownTour: function(aWindow, aWindowClosing = false) {
- log.debug("teardownTour: aWindowClosing = " + aWindowClosing);
- aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
- aWindow.removeEventListener("SSWindowClosing", this);
-
- let originTabs = this.originTabs.get(aWindow);
- if (originTabs) {
- for (let tab of originTabs) {
- tab.removeEventListener("TabClose", this);
- tab.removeEventListener("TabBecomingWindow", this);
- }
- }
- this.originTabs.delete(aWindow);
-
- if (!aWindowClosing) {
- this.hideHighlight(aWindow);
- this.hideInfo(aWindow);
- // Ensure the menu panel is hidden before calling recreatePopup so popup events occur.
- this.hideMenu(aWindow, "appMenu");
- this.hideMenu(aWindow, "loop");
- }
-
- // Clean up panel listeners after we may have called hideMenu above.
- aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hideAppMenuAnnotations);
- aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hideAppMenuAnnotations);
- aWindow.PanelUI.panel.removeEventListener("popuphidden", this.onPanelHidden);
- let loopPanel = aWindow.document.getElementById("loop-notification-panel");
- loopPanel.removeEventListener("popuphidden", this.onPanelHidden);
- loopPanel.removeEventListener("popuphiding", this.hideLoopPanelAnnotations);
-
- this.endUrlbarCapture(aWindow);
- this.removePinnedTab(aWindow);
- this.resetTheme();
- },
-
- getChromeWindow: function(aContentDocument) {
- return aContentDocument.defaultView
- .window
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow)
- .wrappedJSObject;
- },
-
- // This function is copied to UITourListener.
- isSafeScheme: function(aURI) {
- let allowedSchemes = new Set(["https", "about"]);
- if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
- allowedSchemes.add("http");
-
- if (!allowedSchemes.has(aURI.scheme)) {
- log.error("Unsafe scheme:", aURI.scheme);
- return false;
- }
-
- return true;
- },
-
- resolveURL: function(aBrowser, aURL) {
- try {
- let uri = Services.io.newURI(aURL, null, aBrowser.currentURI);
-
- if (!this.isSafeScheme(uri))
- return null;
-
- return uri.spec;
- } catch (e) {}
-
- return null;
- },
-
- sendPageCallback: function(aMessageManager, aCallbackID, aData = {}) {
- let detail = {data: aData, callbackID: aCallbackID};
- log.debug("sendPageCallback", detail);
- aMessageManager.sendAsyncMessage("UITour:SendPageCallback", detail);
- },
-
- isElementVisible: function(aElement) {
- let targetStyle = aElement.ownerDocument.defaultView.getComputedStyle(aElement);
- return !aElement.ownerDocument.hidden &&
- targetStyle.display != "none" &&
- targetStyle.visibility == "visible";
- },
-
- getTarget: function(aWindow, aTargetName, aSticky = false) {
- log.debug("getTarget:", aTargetName);
- let deferred = Promise.defer();
- if (typeof aTargetName != "string" || !aTargetName) {
- log.warn("getTarget: Invalid target name specified");
- deferred.reject("Invalid target name specified");
- return deferred.promise;
- }
-
- if (aTargetName == "pinnedTab") {
- deferred.resolve({
- targetName: aTargetName,
- node: this.ensurePinnedTab(aWindow, aSticky)
- });
- return deferred.promise;
- }
-
- if (aTargetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
- let engineID = aTargetName.slice(TARGET_SEARCHENGINE_PREFIX.length);
- return this.getSearchEngineTarget(aWindow, engineID);
- }
-
- let targetObject = this.targets.get(aTargetName);
- if (!targetObject) {
- log.warn("getTarget: The specified target name is not in the allowed set");
- deferred.reject("The specified target name is not in the allowed set");
- return deferred.promise;
- }
-
- let targetQuery = targetObject.query;
- aWindow.PanelUI.ensureReady().then(() => {
- let node;
- if (typeof targetQuery == "function") {
- try {
- node = targetQuery(aWindow.document);
- } catch (ex) {
- log.warn("getTarget: Error running target query:", ex);
- node = null;
- }
- } else {
- node = aWindow.document.querySelector(targetQuery);
- }
-
- deferred.resolve({
- addTargetListener: targetObject.addTargetListener,
- infoPanelOffsetX: targetObject.infoPanelOffsetX,
- infoPanelOffsetY: targetObject.infoPanelOffsetY,
- infoPanelPosition: targetObject.infoPanelPosition,
- node: node,
- removeTargetListener: targetObject.removeTargetListener,
- targetName: aTargetName,
- widgetName: targetObject.widgetName,
- allowAdd: targetObject.allowAdd,
- });
- }).catch(log.error);
- return deferred.promise;
- },
-
- targetIsInAppMenu: function(aTarget) {
- let placement = CustomizableUI.getPlacementOfWidget(aTarget.widgetName || aTarget.node.id);
- if (placement && placement.area == CustomizableUI.AREA_PANEL) {
- return true;
- }
-
- let targetElement = aTarget.node;
- // Use the widget for filtering if it exists since the target may be the icon inside.
- if (aTarget.widgetName) {
- targetElement = aTarget.node.ownerDocument.getElementById(aTarget.widgetName);
- }
-
- // Handle the non-customizable buttons at the bottom of the menu which aren't proper widgets.
- return targetElement.id.startsWith("PanelUI-")
- && targetElement.id != "PanelUI-button";
- },
-
- /**
- * Called before opening or after closing a highlight or info panel to see if
- * we need to open or close the appMenu to see the annotation's anchor.
- */
- _setAppMenuStateForAnnotation: function(aWindow, aAnnotationType, aShouldOpenForHighlight, aCallback = null) {
- log.debug("_setAppMenuStateForAnnotation:", aAnnotationType);
- log.debug("_setAppMenuStateForAnnotation: Menu is expected to be:", aShouldOpenForHighlight ? "open" : "closed");
-
- // If the panel is in the desired state, we're done.
- let panelIsOpen = aWindow.PanelUI.panel.state != "closed";
- if (aShouldOpenForHighlight == panelIsOpen) {
- log.debug("_setAppMenuStateForAnnotation: Panel already in expected state");
- if (aCallback)
- aCallback();
- return;
- }
-
- // Don't close the menu if it wasn't opened by us (e.g. via showmenu instead).
- if (!aShouldOpenForHighlight && !this.appMenuOpenForAnnotation.has(aAnnotationType)) {
- log.debug("_setAppMenuStateForAnnotation: Menu not opened by us, not closing");
- if (aCallback)
- aCallback();
- return;
- }
-
- if (aShouldOpenForHighlight) {
- this.appMenuOpenForAnnotation.add(aAnnotationType);
- } else {
- this.appMenuOpenForAnnotation.delete(aAnnotationType);
- }
-
- // Actually show or hide the menu
- if (this.appMenuOpenForAnnotation.size) {
- log.debug("_setAppMenuStateForAnnotation: Opening the menu");
- this.showMenu(aWindow, "appMenu", aCallback);
- } else {
- log.debug("_setAppMenuStateForAnnotation: Closing the menu");
- this.hideMenu(aWindow, "appMenu");
- if (aCallback)
- aCallback();
- }
-
- },
-
- previewTheme: function(aTheme) {
- let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
- let data = LightweightThemeManager.parseTheme(aTheme, origin);
- if (data)
- LightweightThemeManager.previewTheme(data);
- },
-
- resetTheme: function() {
- LightweightThemeManager.resetPreview();
- },
-
- ensurePinnedTab: function(aWindow, aSticky = false) {
- let tabInfo = this.pinnedTabs.get(aWindow);
-
- if (tabInfo) {
- tabInfo.sticky = tabInfo.sticky || aSticky;
- } else {
- let url = Services.urlFormatter.formatURLPref("browser.uitour.pinnedTabUrl");
-
- let tab = aWindow.gBrowser.addTab(url);
- aWindow.gBrowser.pinTab(tab);
- tab.addEventListener("TabClose", () => {
- this.pinnedTabs.delete(aWindow);
- });
-
- tabInfo = {
- tab: tab,
- sticky: aSticky
- };
- this.pinnedTabs.set(aWindow, tabInfo);
- }
-
- return tabInfo.tab;
- },
-
- removePinnedTab: function(aWindow) {
- let tabInfo = this.pinnedTabs.get(aWindow);
- if (tabInfo)
- aWindow.gBrowser.removeTab(tabInfo.tab);
- },
-
- /**
- * @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
- * are in a sub-frame so the defaultView is not the same as the chrome
- * window.
- * @param aTarget The element to highlight.
- * @param aEffect (optional) The effect to use from UITour.highlightEffects or "none".
- * @see UITour.highlightEffects
- */
- showHighlight: function(aChromeWindow, aTarget, aEffect = "none") {
- function showHighlightPanel() {
- if (aTarget.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
- // This won't affect normal higlights done via the panel, so we need to
- // manually hide those.
- this.hideHighlight(aChromeWindow);
- aTarget.node.setAttribute("_moz-menuactive", true);
- return;
- }
-
- // Conversely, highlights for search engines are highlighted via CSS
- // rather than a panel, so need to be manually removed.
- this._hideSearchEngineHighlight(aChromeWindow);
-
- let highlighter = aChromeWindow.document.getElementById("UITourHighlight");
-
- let effect = aEffect;
- if (effect == "random") {
- // Exclude "random" from the randomly selected effects.
- let randomEffect = 1 + Math.floor(Math.random() * (this.highlightEffects.length - 1));
- if (randomEffect == this.highlightEffects.length)
- randomEffect--; // On the order of 1 in 2^62 chance of this happening.
- effect = this.highlightEffects[randomEffect];
- }
- // Toggle the effect attribute to "none" and flush layout before setting it so the effect plays.
- highlighter.setAttribute("active", "none");
- aChromeWindow.getComputedStyle(highlighter).animationName;
- highlighter.setAttribute("active", effect);
- highlighter.parentElement.setAttribute("targetName", aTarget.targetName);
- highlighter.parentElement.hidden = false;
-
- let highlightAnchor;
- // If the target is in the overflow panel, just highlight the overflow button.
- if (aTarget.node.getAttribute("overflowedItem")) {
- let doc = aTarget.node.ownerDocument;
- let placement = CustomizableUI.getPlacementOfWidget(aTarget.widgetName || aTarget.node.id);
- let areaNode = doc.getElementById(placement.area);
- highlightAnchor = areaNode.overflowable._chevron;
- } else {
- highlightAnchor = aTarget.node;
- }
- let targetRect = highlightAnchor.getBoundingClientRect();
- let highlightHeight = targetRect.height;
- let highlightWidth = targetRect.width;
- let minDimension = Math.min(highlightHeight, highlightWidth);
- let maxDimension = Math.max(highlightHeight, highlightWidth);
-
- // If the dimensions are within 200% of each other (to include the bookmarks button),
- // make the highlight a circle with the largest dimension as the diameter.
- if (maxDimension / minDimension <= 3.0) {
- highlightHeight = highlightWidth = maxDimension;
- highlighter.style.borderRadius = "100%";
- } else {
- highlighter.style.borderRadius = "";
- }
-
- highlighter.style.height = highlightHeight + "px";
- highlighter.style.width = highlightWidth + "px";
-
- // Close a previous highlight so we can relocate the panel.
- if (highlighter.parentElement.state == "showing" || highlighter.parentElement.state == "open") {
- log.debug("showHighlight: Closing previous highlight first");
- highlighter.parentElement.hidePopup();
- }
- /* The "overlap" position anchors from the top-left but we want to centre highlights at their
- minimum size. */
- let highlightWindow = aChromeWindow;
- let containerStyle = highlightWindow.getComputedStyle(highlighter.parentElement);
- let paddingTopPx = 0 - parseFloat(containerStyle.paddingTop);
- let paddingLeftPx = 0 - parseFloat(containerStyle.paddingLeft);
- let highlightStyle = highlightWindow.getComputedStyle(highlighter);
- let highlightHeightWithMin = Math.max(highlightHeight, parseFloat(highlightStyle.minHeight));
- let highlightWidthWithMin = Math.max(highlightWidth, parseFloat(highlightStyle.minWidth));
- let offsetX = paddingTopPx
- - (Math.max(0, highlightWidthWithMin - targetRect.width) / 2);
- let offsetY = paddingLeftPx
- - (Math.max(0, highlightHeightWithMin - targetRect.height) / 2);
- this._addAnnotationPanelMutationObserver(highlighter.parentElement);
- highlighter.parentElement.openPopup(highlightAnchor, "overlap", offsetX, offsetY);
- }
-
- // Prevent showing a panel at an undefined position.
- if (!this.isElementVisible(aTarget.node)) {
- log.warn("showHighlight: Not showing a highlight since the target isn't visible", aTarget);
- return;
- }
-
- this._setAppMenuStateForAnnotation(aChromeWindow, "highlight",
- this.targetIsInAppMenu(aTarget),
- showHighlightPanel.bind(this));
- },
-
- hideHighlight: function(aWindow) {
- let tabData = this.pinnedTabs.get(aWindow);
- if (tabData && !tabData.sticky)
- this.removePinnedTab(aWindow);
-
- let highlighter = aWindow.document.getElementById("UITourHighlight");
- this._removeAnnotationPanelMutationObserver(highlighter.parentElement);
- highlighter.parentElement.hidePopup();
- highlighter.removeAttribute("active");
-
- this._setAppMenuStateForAnnotation(aWindow, "highlight", false);
- this._hideSearchEngineHighlight(aWindow);
- },
-
- _hideSearchEngineHighlight: function(aWindow) {
- // We special case highlighting items in the search engines dropdown,
- // so just blindly remove any highlight there.
- let searchMenuBtn = null;
- try {
- searchMenuBtn = this.targets.get("searchProvider").query(aWindow.document);
- } catch (e) { /* This is ok to fail. */ }
- if (searchMenuBtn) {
- let searchPopup = aWindow.document
- .getAnonymousElementByAttribute(searchMenuBtn,
- "anonid",
- "searchbar-popup");
- for (let menuItem of searchPopup.children)
- menuItem.removeAttribute("_moz-menuactive");
- }
- },
-
- /**
- * Show an info panel.
- *
- * @param {ChromeWindow} aChromeWindow
- * @param {nsIMessageSender} aMessageManager
- * @param {Node} aAnchor
- * @param {String} [aTitle=""]
- * @param {String} [aDescription=""]
- * @param {String} [aIconURL=""]
- * @param {Object[]} [aButtons=[]]
- * @param {Object} [aOptions={}]
- * @param {String} [aOptions.closeButtonCallbackID]
- */
- showInfo: function(aChromeWindow, aMessageManager, aAnchor, aTitle = "", aDescription = "", aIconURL = "",
- aButtons = [], aOptions = {}) {
- function showInfoPanel(aAnchorEl) {
- aAnchorEl.focus();
-
- let document = aChromeWindow.document;
- let tooltip = document.getElementById("UITourTooltip");
- let tooltipTitle = document.getElementById("UITourTooltipTitle");
- let tooltipDesc = document.getElementById("UITourTooltipDescription");
- let tooltipIcon = document.getElementById("UITourTooltipIcon");
- let tooltipIconContainer = document.getElementById("UITourTooltipIconContainer");
- let tooltipButtons = document.getElementById("UITourTooltipButtons");
-
- if (tooltip.state == "showing" || tooltip.state == "open") {
- tooltip.hidePopup();
- }
-
- tooltipTitle.textContent = aTitle || "";
- tooltipDesc.textContent = aDescription || "";
- tooltipIcon.src = aIconURL || "";
- tooltipIconContainer.hidden = !aIconURL;
-
- while (tooltipButtons.firstChild)
- tooltipButtons.firstChild.remove();
-
- for (let button of aButtons) {
- let el = document.createElement("button");
- el.setAttribute("label", button.label);
- if (button.iconURL)
- el.setAttribute("image", button.iconURL);
-
- if (button.style == "link")
- el.setAttribute("class", "button-link");
-
- if (button.style == "primary")
- el.setAttribute("class", "button-primary");
-
- let callbackID = button.callbackID;
- el.addEventListener("command", event => {
- tooltip.hidePopup();
- this.sendPageCallback(aMessageManager, callbackID);
- });
-
- tooltipButtons.appendChild(el);
- }
-
- tooltipButtons.hidden = !aButtons.length;
-
- let tooltipClose = document.getElementById("UITourTooltipClose");
- let closeButtonCallback = (event) => {
- this.hideInfo(document.defaultView);
- if (aOptions && aOptions.closeButtonCallbackID)
- this.sendPageCallback(aMessageManager, aOptions.closeButtonCallbackID);
- };
- tooltipClose.addEventListener("command", closeButtonCallback);
-
- let targetCallback = (event) => {
- let details = {
- target: aAnchor.targetName,
- type: event.type,
- };
- this.sendPageCallback(aMessageManager, aOptions.targetCallbackID, details);
- };
- if (aOptions.targetCallbackID && aAnchor.addTargetListener) {
- aAnchor.addTargetListener(document, targetCallback);
- }
-
- tooltip.addEventListener("popuphiding", function tooltipHiding(event) {
- tooltip.removeEventListener("popuphiding", tooltipHiding);
- tooltipClose.removeEventListener("command", closeButtonCallback);
- if (aOptions.targetCallbackID && aAnchor.removeTargetListener) {
- aAnchor.removeTargetListener(document, targetCallback);
- }
- });
-
- tooltip.setAttribute("targetName", aAnchor.targetName);
- tooltip.hidden = false;
- let alignment = "bottomcenter topright";
- if (aAnchor.infoPanelPosition) {
- alignment = aAnchor.infoPanelPosition;
- }
-
- let { infoPanelOffsetX: xOffset, infoPanelOffsetY: yOffset } = aAnchor;
-
- this._addAnnotationPanelMutationObserver(tooltip);
- tooltip.openPopup(aAnchorEl, alignment, xOffset || 0, yOffset || 0);
- if (tooltip.state == "closed") {
- document.defaultView.addEventListener("endmodalstate", function endModalStateHandler() {
- document.defaultView.removeEventListener("endmodalstate", endModalStateHandler);
- tooltip.openPopup(aAnchorEl, alignment);
- }, false);
- }
- }
-
- // Prevent showing a panel at an undefined position.
- if (!this.isElementVisible(aAnchor.node)) {
- log.warn("showInfo: Not showing since the target isn't visible", aAnchor);
- return;
- }
-
- // Due to a platform limitation, we can't anchor a panel to an element in a
- // . So we can't support showing info panels for search engines.
- if (aAnchor.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX))
- return;
-
- this._setAppMenuStateForAnnotation(aChromeWindow, "info",
- this.targetIsInAppMenu(aAnchor),
- showInfoPanel.bind(this, aAnchor.node));
- },
-
- hideInfo: function(aWindow) {
- let document = aWindow.document;
-
- let tooltip = document.getElementById("UITourTooltip");
- this._removeAnnotationPanelMutationObserver(tooltip);
- tooltip.hidePopup();
- this._setAppMenuStateForAnnotation(aWindow, "info", false);
-
- let tooltipButtons = document.getElementById("UITourTooltipButtons");
- while (tooltipButtons.firstChild)
- tooltipButtons.firstChild.remove();
- },
-
- showMenu: function(aWindow, aMenuName, aOpenCallback = null) {
- function openMenuButton(aMenuBtn) {
- if (!aMenuBtn || !aMenuBtn.boxObject || aMenuBtn.open) {
- if (aOpenCallback)
- aOpenCallback();
- return;
- }
- if (aOpenCallback)
- aMenuBtn.addEventListener("popupshown", onPopupShown);
- aMenuBtn.boxObject.openMenu(true);
- }
- function onPopupShown(event) {
- this.removeEventListener("popupshown", onPopupShown);
- aOpenCallback(event);
- }
-
- if (aMenuName == "appMenu") {
- aWindow.PanelUI.panel.setAttribute("noautohide", "true");
- // If the popup is already opened, don't recreate the widget as it may cause a flicker.
- if (aWindow.PanelUI.panel.state != "open") {
- this.recreatePopup(aWindow.PanelUI.panel);
- }
- aWindow.PanelUI.panel.addEventListener("popuphiding", this.hideAppMenuAnnotations);
- aWindow.PanelUI.panel.addEventListener("ViewShowing", this.hideAppMenuAnnotations);
- aWindow.PanelUI.panel.addEventListener("popuphidden", this.onPanelHidden);
- if (aOpenCallback) {
- aWindow.PanelUI.panel.addEventListener("popupshown", onPopupShown);
- }
- aWindow.PanelUI.show();
- } else if (aMenuName == "bookmarks") {
- let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
- openMenuButton(menuBtn);
- } else if (aMenuName == "loop") {
- let toolbarButton = aWindow.LoopUI.toolbarButton;
- // It's possible to have a node that isn't placed anywhere
- if (!toolbarButton || !toolbarButton.node ||
- !CustomizableUI.getPlacementOfWidget(toolbarButton.node.id)) {
- log.debug("Can't show the Loop menu since the toolbarButton isn't placed");
- return;
- }
-
- let panel = aWindow.document.getElementById("loop-notification-panel");
- panel.setAttribute("noautohide", true);
- if (panel.state != "open") {
- this.recreatePopup(panel);
- this.availableTargetsCache.clear();
- }
-
- // An event object is expected but we don't want to toggle the panel with a click if the panel
- // is already open.
- aWindow.LoopUI.openCallPanel({ target: toolbarButton.node, }, "rooms").then(() => {
- if (aOpenCallback) {
- aOpenCallback();
- }
- });
- panel.addEventListener("popuphidden", this.onPanelHidden);
- panel.addEventListener("popuphiding", this.hideLoopPanelAnnotations);
- } else if (aMenuName == "searchEngines") {
- this.getTarget(aWindow, "searchProvider").then(target => {
- openMenuButton(target.node);
- }).catch(log.error);
- }
- },
-
- hideMenu: function(aWindow, aMenuName) {
- function closeMenuButton(aMenuBtn) {
- if (aMenuBtn && aMenuBtn.boxObject)
- aMenuBtn.boxObject.openMenu(false);
- }
-
- if (aMenuName == "appMenu") {
- aWindow.PanelUI.hide();
- } else if (aMenuName == "bookmarks") {
- let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
- closeMenuButton(menuBtn);
- } else if (aMenuName == "loop") {
- let panel = aWindow.document.getElementById("loop-notification-panel");
- panel.hidePopup();
- } else if (aMenuName == "searchEngines") {
- let menuBtn = this.targets.get("searchProvider").query(aWindow.document);
- closeMenuButton(menuBtn);
- }
- },
-
- hideAnnotationsForPanel: function(aEvent, aTargetPositionCallback) {
- let win = aEvent.target.ownerDocument.defaultView;
- let annotationElements = new Map([
- // [annotationElement (panel), method to hide the annotation]
- [win.document.getElementById("UITourHighlightContainer"), UITour.hideHighlight.bind(UITour)],
- [win.document.getElementById("UITourTooltip"), UITour.hideInfo.bind(UITour)],
- ]);
- annotationElements.forEach((hideMethod, annotationElement) => {
- if (annotationElement.state != "closed") {
- let targetName = annotationElement.getAttribute("targetName");
- UITour.getTarget(win, targetName).then((aTarget) => {
- // Since getTarget is async, we need to make sure that the target hasn't
- // changed since it may have just moved to somewhere outside of the app menu.
- if (annotationElement.getAttribute("targetName") != aTarget.targetName ||
- annotationElement.state == "closed" ||
- !aTargetPositionCallback(aTarget)) {
- return;
- }
- hideMethod(win);
- }).catch(log.error);
- }
- });
- UITour.appMenuOpenForAnnotation.clear();
- },
-
- hideAppMenuAnnotations: function(aEvent) {
- UITour.hideAnnotationsForPanel(aEvent, UITour.targetIsInAppMenu);
- },
-
- hideLoopPanelAnnotations: function(aEvent) {
- UITour.hideAnnotationsForPanel(aEvent, (aTarget) => {
- return aTarget.targetName.startsWith("loop-") && aTarget.targetName != "loop-selectedRoomButtons";
- });
- },
-
- onPanelHidden: function(aEvent) {
- aEvent.target.removeAttribute("noautohide");
- UITour.recreatePopup(aEvent.target);
- },
-
- recreatePopup: function(aPanel) {
- // After changing popup attributes that relate to how the native widget is created
- // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect.
- if (aPanel.hidden) {
- // If the panel is already hidden, we don't need to recreate it but flush
- // in case someone just hid it.
- aPanel.clientWidth; // flush
- return;
- }
- aPanel.hidden = true;
- aPanel.clientWidth; // flush
- aPanel.hidden = false;
- },
-
- startUrlbarCapture: function(aWindow, aExpectedText, aUrl) {
- let urlbar = aWindow.document.getElementById("urlbar");
- this.urlbarCapture.set(aWindow, {
- expected: aExpectedText.toLocaleLowerCase(),
- url: aUrl
- });
- urlbar.addEventListener("input", this);
- },
-
- endUrlbarCapture: function(aWindow) {
- let urlbar = aWindow.document.getElementById("urlbar");
- urlbar.removeEventListener("input", this);
- this.urlbarCapture.delete(aWindow);
- },
-
- handleUrlbarInput: function(aWindow) {
- if (!this.urlbarCapture.has(aWindow))
- return;
-
- let urlbar = aWindow.document.getElementById("urlbar");
-
- let {expected, url} = this.urlbarCapture.get(aWindow);
-
- if (urlbar.value.toLocaleLowerCase().localeCompare(expected) != 0)
- return;
-
- urlbar.handleRevert();
-
- let tab = aWindow.gBrowser.addTab(url, {
- owner: aWindow.gBrowser.selectedTab,
- relatedToCurrent: true
- });
- aWindow.gBrowser.selectedTab = tab;
- },
-
- getConfiguration: function(aMessageManager, aWindow, aConfiguration, aCallbackID) {
- switch (aConfiguration) {
- case "availableTargets":
- this.getAvailableTargets(aMessageManager, aWindow, aCallbackID);
- break;
- case "sync":
- this.sendPageCallback(aMessageManager, aCallbackID, {
- setup: Services.prefs.prefHasUserValue("services.sync.username"),
- });
- break;
- case "appinfo":
- let props = ["defaultUpdateChannel", "version"];
- let appinfo = {};
- props.forEach(property => appinfo[property] = Services.appinfo[property]);
- this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
- break;
- case "selectedSearchEngine":
- Services.search.init(rv => {
- let engine;
- if (Components.isSuccessCode(rv)) {
- engine = Services.search.defaultEngine;
- } else {
- engine = { identifier: "" };
- }
- this.sendPageCallback(aMessageManager, aCallbackID, {
- searchEngineIdentifier: engine.identifier
- });
- });
- break;
- default:
- log.error("getConfiguration: Unknown configuration requested: " + aConfiguration);
- break;
- }
- },
-
- setConfiguration: function(aConfiguration, aValue) {
- switch (aConfiguration) {
- case "Loop:ResumeTourOnFirstJoin":
- // Ignore aValue in this case to avoid accidentally setting it to false.
- Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
- break;
- default:
- log.error("setConfiguration: Unknown configuration requested: " + aConfiguration);
- break;
- }
- },
-
- getAvailableTargets: function(aMessageManager, aChromeWindow, aCallbackID) {
- Task.spawn(function*() {
- let window = aChromeWindow;
- let data = this.availableTargetsCache.get(window);
- if (data) {
- log.debug("getAvailableTargets: Using cached targets list", data.targets.join(","));
- this.sendPageCallback(aMessageManager, aCallbackID, data);
- return;
- }
-
- let promises = [];
- for (let targetName of this.targets.keys()) {
- promises.push(this.getTarget(window, targetName));
- }
- let targetObjects = yield Promise.all(promises);
-
- let targetNames = [
- "pinnedTab",
- ];
-
- for (let targetObject of targetObjects) {
- if (targetObject.node)
- targetNames.push(targetObject.targetName);
- }
-
- targetNames = targetNames.concat(
- yield this.getAvailableSearchEngineTargets(window)
- );
-
- data = {
- targets: targetNames,
- };
- this.availableTargetsCache.set(window, data);
- this.sendPageCallback(aMessageManager, aCallbackID, data);
- }.bind(this)).catch(err => {
- log.error(err);
- this.sendPageCallback(aMessageManager, aCallbackID, {
- targets: [],
- });
- });
- },
-
- addNavBarWidget: function (aTarget, aMessageManager, aCallbackID) {
- if (aTarget.node) {
- log.error("addNavBarWidget: can't add a widget already present:", aTarget);
- return;
- }
- if (!aTarget.allowAdd) {
- log.error("addNavBarWidget: not allowed to add this widget:", aTarget);
- return;
- }
- if (!aTarget.widgetName) {
- log.error("addNavBarWidget: can't add a widget without a widgetName property:", aTarget);
- return;
- }
-
- CustomizableUI.addWidgetToArea(aTarget.widgetName, CustomizableUI.AREA_NAVBAR);
- this.sendPageCallback(aMessageManager, aCallbackID);
- },
-
- _addAnnotationPanelMutationObserver: function(aPanelEl) {
-#ifdef XP_LINUX
- let observer = this._annotationPanelMutationObservers.get(aPanelEl);
- if (observer) {
- return;
- }
- let win = aPanelEl.ownerDocument.defaultView;
- observer = new win.MutationObserver(this._annotationMutationCallback);
- this._annotationPanelMutationObservers.set(aPanelEl, observer);
- let observerOptions = {
- attributeFilter: ["height", "width"],
- attributes: true,
- };
- observer.observe(aPanelEl, observerOptions);
-#endif
- },
-
- _removeAnnotationPanelMutationObserver: function(aPanelEl) {
-#ifdef XP_LINUX
- let observer = this._annotationPanelMutationObservers.get(aPanelEl);
- if (observer) {
- observer.disconnect();
- this._annotationPanelMutationObservers.delete(aPanelEl);
- }
-#endif
- },
-
-/**
- * Workaround for Ubuntu panel craziness in bug 970788 where incorrect sizes get passed to
- * nsXULPopupManager::PopupResized and lead to incorrect width and height attributes getting
- * set on the panel.
- */
- _annotationMutationCallback: function(aMutations) {
- for (let mutation of aMutations) {
- // Remove both attributes at once and ignore remaining mutations to be proccessed.
- mutation.target.removeAttribute("width");
- mutation.target.removeAttribute("height");
- return;
- }
- },
-
- selectSearchEngine(aID) {
- return new Promise((resolve, reject) => {
- Services.search.init((rv) => {
- if (!Components.isSuccessCode(rv)) {
- reject("selectSearchEngine: search service init failed: " + rv);
- return;
- }
-
- let engines = Services.search.getVisibleEngines();
- for (let engine of engines) {
- if (engine.identifier == aID) {
- Services.search.defaultEngine = engine;
- return resolve();
- }
- }
- reject("selectSearchEngine could not find engine with given ID");
- });
- });
- },
-
- getAvailableSearchEngineTargets(aWindow) {
- return new Promise(resolve => {
- this.getTarget(aWindow, "search").then(searchTarget => {
- if (!searchTarget.node || this.targetIsInAppMenu(searchTarget))
- return resolve([]);
-
- Services.search.init(() => {
- let engines = Services.search.getVisibleEngines();
- resolve([TARGET_SEARCHENGINE_PREFIX + engine.identifier
- for (engine of engines)
- if (engine.identifier)]);
- });
- }).catch(() => resolve([]));
- });
- },
-
- // We only allow matching based on a search engine's identifier - this gives
- // us a non-changing ID and guarentees we only match against app-provided
- // engines.
- getSearchEngineTarget(aWindow, aIdentifier) {
- return new Promise((resolve, reject) => {
- Task.spawn(function*() {
- let searchTarget = yield this.getTarget(aWindow, "search");
- // We're not supporting having the searchbar in the app-menu, because
- // popups within popups gets crazy. This restriction should be lifted
- // once bug 988151 is implemented, as the page can then be responsible
- // for opening each menu when appropriate.
- if (!searchTarget.node || this.targetIsInAppMenu(searchTarget))
- return reject("Search engine not available");
-
- yield Services.search.init();
-
- let searchPopup = searchTarget.node._popup;
- for (let engineNode of searchPopup.children) {
- let engine = engineNode.engine;
- if (engine && engine.identifier == aIdentifier) {
- return resolve({
- targetName: TARGET_SEARCHENGINE_PREFIX + engine.identifier,
- node: engineNode,
- });
- }
- }
- reject("Search engine not available");
- }.bind(this)).catch(() => {
- reject("Search engine not available");
- });
- });
- },
-
- notify(eventName, params) {
- let winEnum = Services.wm.getEnumerator("navigator:browser");
- while (winEnum.hasMoreElements()) {
- let window = winEnum.getNext();
- if (window.closed)
- continue;
-
- let originTabs = this.originTabs.get(window);
- if (!originTabs)
- continue;
-
- for (let tab of originTabs) {
- let messageManager = tab.linkedBrowser.messageManager;
- let detail = {
- event: eventName,
- params: params,
- };
- messageManager.sendAsyncMessage("UITour:SendPageNotification", detail);
- }
- }
- },
-};
-
-this.UITour.init();
-
-/**
- * UITour Health Report
- */
-const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
-
-/**
- * Public API to be called by the UITour code
- */
-const UITourHealthReport = {
- recordTreatmentTag: function(tag, value) {
-#ifdef MOZ_SERVICES_HEALTHREPORT
- Task.spawn(function*() {
- let reporter = Cc["@mozilla.org/datareporting/service;1"]
- .getService()
- .wrappedJSObject
- .healthReporter;
-
- // This can happen if the FHR component of the data reporting service is
- // disabled. This is controlled by a pref that most will never use.
- if (!reporter) {
- return;
- }
-
- yield reporter.onInit();
-
- // Get the UITourMetricsProvider instance from the Health Reporter
- reporter.getProvider("org.mozilla.uitour").recordTreatmentTag(tag, value);
- });
-#endif
- }
-};
-
-this.UITourMetricsProvider = function() {
- Metrics.Provider.call(this);
-}
-
-UITourMetricsProvider.prototype = Object.freeze({
- __proto__: Metrics.Provider.prototype,
-
- name: "org.mozilla.uitour",
-
- measurementTypes: [
- UITourTreatmentMeasurement1,
- ],
-
- recordTreatmentTag: function(tag, value) {
- let m = this.getMeasurement(UITourTreatmentMeasurement1.prototype.name,
- UITourTreatmentMeasurement1.prototype.version);
- let field = tag;
-
- if (this.storage.hasFieldFromMeasurement(m.id, field,
- DAILY_DISCRETE_TEXT_FIELD)) {
- let fieldID = this.storage.fieldIDFromMeasurement(m.id, field);
- return this.enqueueStorageOperation(function recordKnownField() {
- return this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
- }.bind(this));
- }
-
- // Otherwise, we first need to create the field.
- return this.enqueueStorageOperation(function recordField() {
- // This function has to return a promise.
- return Task.spawn(function () {
- let fieldID = yield this.storage.registerField(m.id, field,
- DAILY_DISCRETE_TEXT_FIELD);
- yield this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
- }.bind(this));
- }.bind(this));
- },
-});
-
-function UITourTreatmentMeasurement1() {
- Metrics.Measurement.call(this);
-
- this._serializers = {};
- this._serializers[this.SERIALIZE_JSON] = {
- //singular: We don't need a singular serializer because we have none of this data
- daily: this._serializeJSONDaily.bind(this)
- };
-
-}
-
-UITourTreatmentMeasurement1.prototype = Object.freeze({
- __proto__: Metrics.Measurement.prototype,
-
- name: "treatment",
- version: 1,
-
- // our fields are dynamic
- fields: { },
-
- // We need a custom serializer because the default one doesn't accept unknown fields
- _serializeJSONDaily: function(data) {
- let result = {_v: this.version };
-
- for (let [field, data] of data) {
- result[field] = data;
- }
-
- return result;
- }
-});
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/themes/shared/customizableui/panelUIOverlay.inc.css thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/themes/shared/customizableui/panelUIOverlay.inc.css
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/themes/shared/customizableui/panelUIOverlay.inc.css 2015-01-06 13:14:28.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/themes/shared/customizableui/panelUIOverlay.inc.css 2015-01-07 22:15:22.000000000 +0000
@@ -647,30 +647,32 @@
outline: none;
}
+#PanelUI-update-status {
+ color: black;
+}
+
#PanelUI-update-status[update-status="succeeded"] {
- background-color: hsla(96, 65%, 75%, 0.1);
+ background-color: hsla(96, 65%, 75%, 0.5);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
- background-color: hsla(96, 65%, 75%, 0.4);
+ background-color: hsla(96, 65%, 75%, 0.8);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
- background-color: hsla(96, 65%, 75%, 0.6);
- box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
+ background-color: hsl(96, 65%, 75%);
}
#PanelUI-update-status[update-status="failed"] {
- background-color: hsla(359, 69%, 84%, 0.1);
+ background-color: hsla(359, 69%, 84%, 0.5);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
- background-color: hsla(359, 69%, 84%, 0.4);
+ background-color: hsla(359, 69%, 84%, 0.8);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
- background-color: hsla(359, 69%, 84%, 0.6);
- box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
+ background-color: hsl(359, 69%, 84%);
}
#PanelUI-quit:not([disabled]):hover {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css 2015-01-06 13:14:28.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css 2015-01-07 22:15:22.000000000 +0000
@@ -291,6 +291,11 @@
/**
* Sync migration
*/
+#sync-migrate-upgrade-description {
+ /* description elts need a min-width to wrap correctly - bug 630864? */
+ min-width: 100px
+}
+
#sync-migration {
border: 1px solid rgba(0, 0, 0, 0.32);
background-color: InfoBackground;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/build/autoconf/android.m4 thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/build/autoconf/android.m4
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/build/autoconf/android.m4 2015-01-06 13:14:28.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/build/autoconf/android.m4 2015-01-07 22:15:22.000000000 +0000
@@ -353,6 +353,13 @@
if test -z "$android_build_tools" ; then
android_build_tools="$android_platform_tools" # SDK Tools < r22
fi
+ all_android_build_tools=""
+ for suffix in `ls "$android_sdk_root/build-tools" | sed -e "s,android-,999.," | sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr`; do
+ tools_directory=`echo "$android_sdk_root/build-tools/$suffix" | sed -e "s,999.,android-,"`
+ if test -d "$tools_directory" -a -f "$tools_directory/aapt"; then
+ all_android_build_tools="$all_android_build_tools:$tools_directory"
+ fi
+ done
if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then
AC_MSG_RESULT([$android_build_tools])
@@ -390,7 +397,7 @@
dnl Google has a history of moving the Android tools around. We don't
dnl care where they are, so let's try to find them anywhere we can.
- ALL_ANDROID_TOOLS_PATHS="$ANDROID_TOOLS:$ANDROID_BUILD_TOOLS:$ANDROID_PLATFORM_TOOLS"
+ ALL_ANDROID_TOOLS_PATHS="$ANDROID_TOOLS$all_android_build_tools:$ANDROID_PLATFORM_TOOLS"
MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$ALL_ANDROID_TOOLS_PATHS])
MOZ_PATH_PROG(DX, dx, :, [$ALL_ANDROID_TOOLS_PATHS])
MOZ_PATH_PROG(AAPT, aapt, :, [$ALL_ANDROID_TOOLS_PATHS])
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/build/autoconf/clang-plugin.m4 thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/build/autoconf/clang-plugin.m4
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/build/autoconf/clang-plugin.m4 2015-01-06 13:14:28.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/build/autoconf/clang-plugin.m4 2015-01-07 22:15:22.000000000 +0000
@@ -33,7 +33,11 @@
AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin])
fi
LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags`
- LLVM_LDFLAGS=`$LLVMCONFIG --ldflags --system-libs --libs core mc analysis asmparser mcparser bitreader option | xargs`
+ dnl The clang package we use on OSX is old, and its llvm-config doesn't
+ dnl recognize --system-libs, so ask for that separately. llvm-config's
+ dnl failure here is benign, so we can ignore it if it happens.
+ LLVM_LDFLAGS=`$LLVMCONFIG --system-libs | xargs`
+ LLVM_LDFLAGS="$LLVM_LDFLAGS `$LLVMCONFIG --ldflags --libs core mc analysis asmparser mcparser bitreader option | xargs`"
if test "${OS_ARCH}" = "Darwin"; then
CLANG_LDFLAGS="-lclangFrontend -lclangDriver -lclangSerialization"
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/build/autoconf/toolchain.m4 thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/build/autoconf/toolchain.m4
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/build/autoconf/toolchain.m4 2015-01-06 13:14:28.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/build/autoconf/toolchain.m4 2015-01-07 22:15:22.000000000 +0000
@@ -91,6 +91,13 @@
AC_SUBST(CLANG_CXX)
AC_SUBST(CLANG_CL)
+
+if test -n "$GNU_CC" -a -z "$CLANG_CC" ; then
+ if test "$GCC_MAJOR_VERSION" -eq 4 -a "$GCC_MINOR_VERSION" -lt 4 ||
+ test "$GCC_MAJOR_VERSION" -lt 4; then
+ AC_MSG_ERROR([Only GCC 4.4 or newer supported])
+ fi
+fi
])
AC_DEFUN([MOZ_CROSS_COMPILER],
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/chrome/test/moz.build thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/chrome/test/moz.build
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/chrome/test/moz.build 2015-01-06 13:14:29.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/chrome/test/moz.build 2015-01-07 22:15:23.000000000 +0000
@@ -4,8 +4,7 @@
# 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/.
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
-
-# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
-if CONFIG['OS_ARCH'] != 'Darwin':
- XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
+XPCSHELL_TESTS_MANIFESTS += [
+ 'unit/xpcshell.ini',
+ 'unit_ipc/xpcshell.ini',
+]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/configure.in thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/configure.in
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/configure.in 2015-01-06 13:14:29.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/configure.in 2015-01-07 22:15:23.000000000 +0000
@@ -405,13 +405,6 @@
MOZ_CHECK_COMPILER_WRAPPER
-if test -n "$GNU_CC" -a -z "$CLANG_CC" ; then
- if test "$GCC_MAJOR_VERSION" -eq 4 -a "$GCC_MINOR_VERSION" -lt 4 ||
- test "$GCC_MAJOR_VERSION" -lt 4; then
- AC_MSG_ERROR([Only GCC 4.4 or newer supported])
- fi
-fi
-
dnl ========================================================
dnl Special win32 checks
dnl ========================================================
@@ -494,12 +487,7 @@
AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
- if test "$_CC_MAJOR_VERSION" = "17"; then
- _CC_SUITE=11
- MSVS_VERSION=2012
- MSVC_C_RUNTIME_DLL=msvcr110.dll
- MSVC_CXX_RUNTIME_DLL=msvcp110.dll
- elif test "$_CC_MAJOR_VERSION" = "18"; then
+ if test "$_CC_MAJOR_VERSION" = "18"; then
_CC_SUITE=12
MSVS_VERSION=2013
MSVC_C_RUNTIME_DLL=msvcr120.dll
@@ -1252,11 +1240,11 @@
dnl ========================================================
dnl Suppress Clang Argument Warnings
dnl ========================================================
-if test -n "$CLANG_CC"; then
+if test -n "${CLANG_CC}${CLANG_CL}"; then
_WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
fi
-if test -n "$CLANG_CXX"; then
+if test -n "${CLANG_CXX}${CLANG_CL}"; then
_WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
fi
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/nsDocShell.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/nsDocShell.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/nsDocShell.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/nsDocShell.cpp 2015-01-07 22:15:23.000000000 +0000
@@ -838,6 +838,7 @@
mAllowKeywordFixup(false),
mIsOffScreenBrowser(false),
mIsActive(true),
+ mIsPrerendered(false),
mIsAppTab(false),
mUseGlobalHistory(false),
mInPrivateBrowsing(false),
@@ -3386,6 +3387,11 @@
{
SetIsActive(value);
}
+ if (NS_SUCCEEDED(parentAsDocShell->GetIsPrerendered(&value))) {
+ if (value) {
+ SetIsPrerendered(true);
+ }
+ }
if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
value = false;
}
@@ -6053,6 +6059,22 @@
}
NS_IMETHODIMP
+nsDocShell::SetIsPrerendered(bool aPrerendered)
+{
+ MOZ_ASSERT(!aPrerendered || !mIsPrerendered,
+ "SetIsPrerendered(true) called on already prerendered docshell");
+ mIsPrerendered = aPrerendered;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsPrerendered(bool *aIsPrerendered)
+{
+ *aIsPrerendered = mIsPrerendered;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsDocShell::SetIsAppTab(bool aIsAppTab)
{
mIsAppTab = aIsAppTab;
@@ -8553,8 +8575,8 @@
// this.AddChild(child) calls child.SetDocLoaderParent(this), meaning
// that the child inherits our state. Among other things, this means
- // that the child inherits our mIsActive and mInPrivateBrowsing, which
- // is what we want.
+ // that the child inherits our mIsActive, mIsPrerendered and mInPrivateBrowsing,
+ // which is what we want.
AddChild(childItem);
childShell->SetAllowPlugins(allowPlugins);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/nsDocShell.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/nsDocShell.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/nsDocShell.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/nsDocShell.h 2015-01-07 22:15:23.000000000 +0000
@@ -877,6 +877,7 @@
bool mAllowKeywordFixup;
bool mIsOffScreenBrowser;
bool mIsActive;
+ bool mIsPrerendered;
bool mIsAppTab;
bool mUseGlobalHistory;
bool mInPrivateBrowsing;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/nsIDocShell.idl thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/nsIDocShell.idl
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/nsIDocShell.idl 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/nsIDocShell.idl 2015-01-07 22:15:23.000000000 +0000
@@ -54,7 +54,7 @@
typedef unsigned long nsLoadFlags;
-[scriptable, builtinclass, uuid(c2756385-bc54-417b-9ae4-c5a40053a2a3)]
+[scriptable, builtinclass, uuid(fef3bae1-6673-4c49-9f5a-fcc075926730)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@@ -620,6 +620,13 @@
attribute boolean isActive;
/**
+ * Puts the docshell in prerendering mode. noscript because we want only
+ * native code to be able to put a docshell in prerendering.
+ */
+ [noscript] void SetIsPrerendered(in boolean prerendered);
+ readonly attribute boolean isPrerendered;
+
+ /**
* The ID of the docshell in the session history.
*/
readonly attribute unsigned long long historyID;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/TimelineMarker.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/TimelineMarker.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/base/TimelineMarker.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/base/TimelineMarker.h 2015-01-07 22:15:23.000000000 +0000
@@ -103,10 +103,9 @@
DOMHighResTimeStamp mTime;
nsString mCause;
- // While normally it is not a good idea to make a persistent
- // root, in this case changing nsDocShell to participate in
- // cycle collection was deemed too invasive, the stack trace
- // can't actually cause a cycle, and the markers are only held
+ // While normally it is not a good idea to make a persistent root,
+ // in this case changing nsDocShell to participate in cycle
+ // collection was deemed too invasive, and the markers are only held
// here temporarily to boot.
mozilla::Maybe> mStackTrace;
};
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/test/moz.build thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/test/moz.build
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/docshell/test/moz.build 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/docshell/test/moz.build 2015-01-07 22:15:23.000000000 +0000
@@ -4,11 +4,10 @@
# 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/.
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
-
-# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
-if CONFIG['OS_ARCH'] != 'Darwin':
- XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
+XPCSHELL_TESTS_MANIFESTS += [
+ 'unit/xpcshell.ini',
+ 'unit_ipc/xpcshell.ini',
+]
MOCHITEST_MANIFESTS += [
'chrome/mochitest.ini',
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/animation/AnimationTimeline.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/animation/AnimationTimeline.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/animation/AnimationTimeline.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/animation/AnimationTimeline.cpp 2015-01-07 22:15:23.000000000 +0000
@@ -60,14 +60,8 @@
return;
}
- // Bug 1113413: If the refresh driver has just been restored from test
- // control it's possible that aTimeStamp could be before the most recent
- // refresh.
- if (refreshDriver &&
- aTimeStamp < refreshDriver->MostRecentRefresh()) {
- mFastForwardTime = refreshDriver->MostRecentRefresh();
- return;
- }
+ MOZ_ASSERT(!refreshDriver || aTimeStamp >= refreshDriver->MostRecentRefresh(),
+ "aTimeStamp must be >= the refresh driver time");
// FIXME: For all animations attached to this timeline, we should mark
// their target elements as needing restyling. Otherwise, tasks that run
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/animation/test/css-transitions/test_animation-player-ready.html thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/animation/test/css-transitions/test_animation-player-ready.html
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/animation/test/css-transitions/test_animation-player-ready.html 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/animation/test/css-transitions/test_animation-player-ready.html 2015-01-07 22:15:23.000000000 +0000
@@ -67,4 +67,36 @@
}, 'ready promise is rejected when a transition is cancelled by updating'
+ ' transition-property');
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // Set up pending transition
+ div.style.marginLeft = '0px';
+ window.getComputedStyle(div).marginLeft;
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '100px';
+ window.getComputedStyle(div).marginLeft;
+
+ var player = div.getAnimationPlayers()[0];
+ assert_equals(player.playState, 'pending', 'Player is initially pending');
+
+ // Set up listeners on ready promise
+ player.ready.then(t.step_func(function() {
+ assert_unreached('ready promise was fulfilled');
+ })).catch(t.step_func(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'ready promise is rejected with AbortError');
+ assert_equals(player.playState, 'idle',
+ 'Player is idle after transition was cancelled');
+ })).then(t.step_func(function() {
+ t.done();
+ }));
+
+ // Now update the transition to animate to something not-interpolable
+ div.style.marginLeft = 'auto';
+ window.getComputedStyle(div).marginLeft;
+
+}, 'ready promise is rejected when a transition is cancelled by changing'
+ + ' the transition property to something not interpolable');
+
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/animation/test/mochitest.ini thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/animation/test/mochitest.ini
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/animation/test/mochitest.ini 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/animation/test/mochitest.ini 2015-01-07 22:15:23.000000000 +0000
@@ -7,7 +7,7 @@
[css-animations/test_animations-dynamic-changes.html]
[css-animations/test_animation-effect-name.html]
[css-animations/test_animation-pausing.html]
-skip-if = os == "mac" && os_version == "10.8" # disabled until bug 1112480 lands
+skip-if = os == "win" || (os == "mac" && os_version == "10.8") # disabled until bug 1112480 lands
[css-animations/test_animation-player-playstate.html]
[css-animations/test_animation-player-ready.html]
[css-animations/test_animation-target.html]
@@ -15,6 +15,7 @@
skip-if = buildapp == 'mulet'
[css-transitions/test_animation-effect-name.html]
[css-transitions/test_animation-pausing.html]
+skip-if = os == "win" # bug 1117955, to be restored in bug 1112480
[css-transitions/test_animation-player-ready.html]
[css-transitions/test_animation-target.html]
[css-transitions/test_element-get-animation-players.html]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/apps/PermissionsTable.jsm thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/apps/PermissionsTable.jsm
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/apps/PermissionsTable.jsm 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/apps/PermissionsTable.jsm 2015-01-07 22:15:23.000000000 +0000
@@ -407,7 +407,13 @@
privileged: PROMPT_ACTION,
certified: ALLOW_ACTION
},
- "nfc": {
+ "audio-capture:3gpp": {
+ app: DENY_ACTION,
+ trusted: DENY_ACTION,
+ privileged: ALLOW_ACTION,
+ certified: ALLOW_ACTION
+ },
+ "nfc": {
app: DENY_ACTION,
trusted: DENY_ACTION,
privileged: ALLOW_ACTION,
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/archivereader/ArchiveZipFile.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/archivereader/ArchiveZipFile.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/archivereader/ArchiveZipFile.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/archivereader/ArchiveZipFile.cpp 2015-01-07 22:15:23.000000000 +0000
@@ -378,9 +378,8 @@
mStart,
mLength,
mCentral);
- NS_ADDREF(stream);
- *aStream = stream;
+ stream.forget(aStream);
return NS_OK;
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/asmjscache/AsmJSCache.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/asmjscache/AsmJSCache.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/asmjscache/AsmJSCache.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/asmjscache/AsmJSCache.cpp 2015-01-07 22:15:23.000000000 +0000
@@ -354,7 +354,7 @@
}
File*
- operator->() const
+ operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
MOZ_ASSERT(mFile);
return mFile;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Attr.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Attr.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Attr.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Attr.h 2015-01-07 22:15:24.000000000 +0000
@@ -79,7 +79,7 @@
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Attr,
nsIAttribute)
- virtual nsIDOMNode* AsDOMNode() { return this; }
+ virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
// WebIDL
virtual JSObject* WrapNode(JSContext* aCx) MOZ_OVERRIDE;
@@ -98,7 +98,7 @@
Element* GetOwnerElement(ErrorResult& aRv);
protected:
- virtual Element* GetNameSpaceElement()
+ virtual Element* GetNameSpaceElement() MOZ_OVERRIDE
{
return GetElement();
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Comment.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Comment.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Comment.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Comment.h 2015-01-07 22:15:24.000000000 +0000
@@ -52,7 +52,7 @@
// Empty interface
// nsINode
- virtual bool IsNodeOfType(uint32_t aFlags) const;
+ virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
virtual nsGenericDOMDataNode* CloneDataNode(mozilla::dom::NodeInfo *aNodeInfo,
bool aCloneText) const MOZ_OVERRIDE;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/ConsoleAPIStorage.js thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/ConsoleAPIStorage.js
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/ConsoleAPIStorage.js 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/ConsoleAPIStorage.js 2015-01-07 22:15:24.000000000 +0000
@@ -11,9 +11,18 @@
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
+// The console API events have to be scheduled when stored using
+// |recordPendingEvent|.
+const CALL_DELAY = 15 // milliseconds
+
+// This constant tells how many messages to process in a single timer execution.
+const MESSAGES_IN_INTERVAL = 1500
+
const STORAGE_MAX_EVENTS = 200;
var _consoleStorage = new Map();
+var _consolePendingStorage = new Map();
+var _timer;
const CONSOLEAPISTORAGE_CID = Components.ID('{96cf7855-dfa9-4c6d-8276-f9705b4890f2}');
@@ -114,7 +123,7 @@
* @param object aEvent
* A JavaScript object you want to store.
*/
- recordEvent: function CS_recordEvent(aId, aEvent)
+ recordEvent: function CS_recordEvent(aId, aOuterId, aEvent)
{
if (!_consoleStorage.has(aId)) {
_consoleStorage.set(aId, []);
@@ -128,10 +137,56 @@
storage.shift();
}
+ Services.obs.notifyObservers(aEvent, "console-api-log-event", aOuterId);
Services.obs.notifyObservers(aEvent, "console-storage-cache-event", aId);
},
/**
+ * Similar to recordEvent, but these events are scheduled and stored any
+ * CALL_DELAY millisecs.
+ */
+ recordPendingEvent: function CS_recordPendingEvent(aId, aOuterId, aEvent)
+ {
+ if (!_consolePendingStorage.has(aId)) {
+ _consolePendingStorage.set(aId, []);
+ }
+
+ let storage = _consolePendingStorage.get(aId);
+ storage.push({ outerId: aOuterId, event: aEvent });
+
+ if (!_timer) {
+ _timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ }
+
+ let self = this;
+ _timer.initWithCallback(function() { self.flushPendingEvents(); },
+ CALL_DELAY, Ci.nsITimer.TYPE_REPEATING_SLACK);
+ },
+
+ /**
+ * Processes the pending event queue.
+ */
+ flushPendingEvents: function CS_flushPendingEvents()
+ {
+ for (let [id, objs] of _consolePendingStorage) {
+ for (let i = 0; i < objs.length && i < MESSAGES_IN_INTERVAL; ++i) {
+ this.recordEvent(id, objs[i].outerId, objs[i].event);
+ }
+
+ if (objs.length <= MESSAGES_IN_INTERVAL) {
+ _consolePendingStorage.delete(id);
+ } else {
+ _consolePendingStorage.set(id, objs.splice(MESSAGES_IN_INTERVAL));
+ }
+ }
+
+ if (_timer && _consolePendingStorage.size == 0) {
+ _timer.cancel();
+ _timer = null;
+ }
+ },
+
+ /**
* Clear storage data for the given window.
*
* @param string [aId]
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Console.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Console.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Console.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Console.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -29,6 +29,7 @@
#include "nsIServiceManager.h"
#include "nsISupportsPrimitives.h"
#include "nsIWebNavigation.h"
+#include "nsIXPConnect.h"
// The maximum allowed number of concurrent timers per page.
#define MAX_PAGE_TIMERS 10000
@@ -40,13 +41,6 @@
// console.trace().
#define DEFAULT_MAX_STACKTRACE_DEPTH 200
-// The console API methods are async and their action is executed later. This
-// delay tells how much later.
-#define CALL_DELAY 15 // milliseconds
-
-// This constant tells how many messages to process in a single timer execution.
-#define MESSAGES_IN_INTERVAL 1500
-
// This tag is used in the Structured Clone Algorithm to move js values from
// worker thread to main thread
#define CONSOLE_TAG JS_SCTAG_USER_MIN
@@ -137,22 +131,17 @@
ConsoleStructuredCloneCallbacksError
};
-class ConsoleCallData MOZ_FINAL : public LinkedListElement
+class ConsoleCallData MOZ_FINAL
{
public:
ConsoleCallData()
: mMethodName(Console::MethodLog)
, mPrivate(false)
, mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
- , mMonotonicTimer(0)
- {
- MOZ_COUNT_CTOR(ConsoleCallData);
- }
-
- ~ConsoleCallData()
- {
- MOZ_COUNT_DTOR(ConsoleCallData);
- }
+ , mIDType(eUnknown)
+ , mOuterIDNumber(0)
+ , mInnerIDNumber(0)
+ { }
void
Initialize(JSContext* aCx, Console::MethodName aName,
@@ -167,6 +156,33 @@
}
}
+ void
+ SetIDs(uint64_t aOuterID, uint64_t aInnerID)
+ {
+ MOZ_ASSERT(mIDType == eUnknown);
+
+ mOuterIDNumber = aOuterID;
+ mInnerIDNumber = aInnerID;
+ mIDType = eNumber;
+ }
+
+ void
+ SetIDs(const nsAString& aOuterID, const nsAString& aInnerID)
+ {
+ MOZ_ASSERT(mIDType == eUnknown);
+
+ mOuterIDString = aOuterID;
+ mInnerIDString = aInnerID;
+ mIDType = eString;
+ }
+
+ void
+ CleanupJSObjects()
+ {
+ mArguments.Clear();
+ mGlobal = nullptr;
+ }
+
JS::Heap mGlobal;
Console::MethodName mMethodName;
@@ -174,6 +190,24 @@
int64_t mTimeStamp;
DOMHighResTimeStamp mMonotonicTimer;
+ // The concept of outerID and innerID is misleading because when a
+ // ConsoleCallData is created from a window, these are the window IDs, but
+ // when the object is created from a SharedWorker, a ServiceWorker or a
+ // subworker of a ChromeWorker these IDs are the type of worker and the
+ // filename of the callee.
+ // In Console.jsm the ID is 'jsm'.
+ enum {
+ eString,
+ eNumber,
+ eUnknown
+ } mIDType;
+
+ uint64_t mOuterIDNumber;
+ nsString mOuterIDString;
+
+ uint64_t mInnerIDNumber;
+ nsString mInnerIDString;
+
nsString mMethodString;
nsTArray> mArguments;
@@ -209,8 +243,9 @@
class ConsoleRunnable : public nsRunnable
{
public:
- ConsoleRunnable()
+ explicit ConsoleRunnable(Console* aConsole)
: mWorkerPrivate(GetCurrentThreadWorkerPrivate())
+ , mConsole(aConsole)
{
MOZ_ASSERT(mWorkerPrivate);
}
@@ -248,7 +283,18 @@
{
AssertIsOnMainThread();
- RunConsole();
+ // Walk up to our containing page
+ WorkerPrivate* wp = mWorkerPrivate;
+ while (wp->GetParent()) {
+ wp = wp->GetParent();
+ }
+
+ nsPIDOMWindow* window = wp->GetWindow();
+ if (!window) {
+ RunWindowless();
+ } else {
+ RunWithWindow(window);
+ }
nsRefPtr response =
new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
@@ -261,15 +307,70 @@
return NS_OK;
}
+ void
+ RunWithWindow(nsPIDOMWindow* aWindow)
+ {
+ AutoJSAPI jsapi;
+ MOZ_ASSERT(aWindow);
+
+ nsRefPtr win = static_cast(aWindow);
+ if (NS_WARN_IF(!jsapi.Init(win))) {
+ return;
+ }
+
+ MOZ_ASSERT(aWindow->IsInnerWindow());
+ nsPIDOMWindow* outerWindow = aWindow->GetOuterWindow();
+ MOZ_ASSERT(outerWindow);
+
+ RunConsole(jsapi.cx(), outerWindow, aWindow);
+ }
+
+ void
+ RunWindowless()
+ {
+ WorkerPrivate* wp = mWorkerPrivate;
+ while (wp->GetParent()) {
+ wp = wp->GetParent();
+ }
+
+ MOZ_ASSERT(!wp->GetWindow());
+
+ AutoSafeJSContext cx;
+
+ nsCOMPtr sandbox =
+ mConsole->GetOrCreateSandbox(cx, wp->GetPrincipal());
+ if (NS_WARN_IF(!sandbox)) {
+ return;
+ }
+
+ JS::Rooted global(cx, sandbox->GetJSObject());
+ if (NS_WARN_IF(!global)) {
+ return;
+ }
+
+ // The CreateSandbox call returns a proxy to the actual sandbox object. We
+ // don't need a proxy here.
+ global = js::UncheckedUnwrap(global);
+
+ JSAutoCompartment ac(cx, global);
+
+ RunConsole(cx, nullptr, nullptr);
+ }
+
protected:
virtual bool
PreDispatch(JSContext* aCx) = 0;
virtual void
- RunConsole() = 0;
+ RunConsole(JSContext* aCx, nsPIDOMWindow* aOuterWindow,
+ nsPIDOMWindow* aInnerWindow) = 0;
WorkerPrivate* mWorkerPrivate;
+ // Raw pointer because this method is async and this object is kept alive by
+ // the caller.
+ Console* mConsole;
+
private:
nsCOMPtr mSyncLoopTarget;
};
@@ -279,15 +380,21 @@
class ConsoleCallDataRunnable MOZ_FINAL : public ConsoleRunnable
{
public:
- explicit ConsoleCallDataRunnable(ConsoleCallData* aCallData)
- : mCallData(aCallData)
- {
- }
+ ConsoleCallDataRunnable(Console* aConsole,
+ ConsoleCallData* aCallData)
+ : ConsoleRunnable(aConsole)
+ , mCallData(aCallData)
+ { }
private:
+ ~ConsoleCallDataRunnable()
+ { }
+
bool
PreDispatch(JSContext* aCx) MOZ_OVERRIDE
{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
ClearException ce(aCx);
JSAutoCompartment ac(aCx, mCallData->mGlobal);
@@ -311,58 +418,67 @@
return false;
}
- mCallData->mArguments.Clear();
- mCallData->mGlobal = nullptr;
+ mCallData->CleanupJSObjects();
return true;
}
void
- RunConsole() MOZ_OVERRIDE
+ RunConsole(JSContext* aCx, nsPIDOMWindow* aOuterWindow,
+ nsPIDOMWindow* aInnerWindow) MOZ_OVERRIDE
{
- // Walk up to our containing page
- WorkerPrivate* wp = mWorkerPrivate;
- while (wp->GetParent()) {
- wp = wp->GetParent();
- }
+ MOZ_ASSERT(NS_IsMainThread());
- nsPIDOMWindow* window = wp->GetWindow();
- NS_ENSURE_TRUE_VOID(window);
+ // The windows have to run in parallel.
+ MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
- nsRefPtr win = static_cast(window);
- NS_ENSURE_TRUE_VOID(win);
+ if (aOuterWindow) {
+ mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
+ } else {
+ ConsoleStackEntry frame;
+ if (mCallData->mTopStackFrame) {
+ frame = *mCallData->mTopStackFrame;
+ }
- AutoJSAPI jsapi;
- if (NS_WARN_IF(!jsapi.Init(win))) {
- return;
- }
- JSContext* cx = jsapi.cx();
- ClearException ce(cx);
+ nsString id;
+ if (mWorkerPrivate->IsSharedWorker()) {
+ id = NS_LITERAL_STRING("SharedWorker");
+ } else if (mWorkerPrivate->IsServiceWorker()) {
+ id = NS_LITERAL_STRING("ServiceWorker");
+ } else {
+ id = NS_LITERAL_STRING("Worker");
+ }
- ErrorResult error;
- nsRefPtr console = win->GetConsole(error);
- if (error.Failed()) {
- NS_WARNING("Failed to get console from the window.");
- return;
+ mCallData->SetIDs(id, frame.mFilename);
}
- JS::Rooted argumentsValue(cx);
- if (!mArguments.read(cx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
+ ProcessCallData(aCx);
+ mCallData->CleanupJSObjects();
+ }
+
+private:
+ void
+ ProcessCallData(JSContext* aCx)
+ {
+ ClearException ce(aCx);
+
+ JS::Rooted argumentsValue(aCx);
+ if (!mArguments.read(aCx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
return;
}
MOZ_ASSERT(argumentsValue.isObject());
- JS::Rooted argumentsObj(cx, &argumentsValue.toObject());
- MOZ_ASSERT(JS_IsArrayObject(cx, argumentsObj));
+ JS::Rooted argumentsObj(aCx, &argumentsValue.toObject());
+ MOZ_ASSERT(JS_IsArrayObject(aCx, argumentsObj));
uint32_t length;
- if (!JS_GetArrayLength(cx, argumentsObj, &length)) {
+ if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
return;
}
for (uint32_t i = 0; i < length; ++i) {
- JS::Rooted value(cx);
+ JS::Rooted value(aCx);
- if (!JS_GetElement(cx, argumentsObj, i, &value)) {
+ if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
return;
}
@@ -371,12 +487,11 @@
MOZ_ASSERT(mCallData->mArguments.Length() == length);
- mCallData->mGlobal = JS::CurrentGlobalOrNull(cx);
- console->AppendCallData(mCallData.forget());
+ mCallData->mGlobal = JS::CurrentGlobalOrNull(aCx);
+ mConsole->ProcessCallData(mCallData);
}
-private:
- nsAutoPtr mCallData;
+ ConsoleCallData* mCallData;
JSAutoStructuredCloneBuffer mArguments;
nsTArray mStrings;
@@ -386,11 +501,13 @@
class ConsoleProfileRunnable MOZ_FINAL : public ConsoleRunnable
{
public:
- ConsoleProfileRunnable(const nsAString& aAction,
+ ConsoleProfileRunnable(Console* aConsole, const nsAString& aAction,
const Sequence& aArguments)
- : mAction(aAction)
+ : ConsoleRunnable(aConsole)
+ , mAction(aAction)
, mArguments(aArguments)
{
+ MOZ_ASSERT(aConsole);
}
private:
@@ -430,64 +547,40 @@
}
void
- RunConsole() MOZ_OVERRIDE
+ RunConsole(JSContext* aCx, nsPIDOMWindow* aOuterWindow,
+ nsPIDOMWindow* aInnerWindow) MOZ_OVERRIDE
{
- // Walk up to our containing page
- WorkerPrivate* wp = mWorkerPrivate;
- while (wp->GetParent()) {
- wp = wp->GetParent();
- }
-
- nsPIDOMWindow* window = wp->GetWindow();
- NS_ENSURE_TRUE_VOID(window);
-
- nsRefPtr win = static_cast(window);
- NS_ENSURE_TRUE_VOID(win);
-
- AutoJSAPI jsapi;
- if (NS_WARN_IF(!jsapi.Init(win))) {
- return;
- }
- JSContext* cx = jsapi.cx();
- ClearException ce(cx);
-
- ErrorResult error;
- nsRefPtr console = win->GetConsole(error);
- if (error.Failed()) {
- NS_WARNING("Failed to get console from the window.");
- return;
- }
+ ClearException ce(aCx);
- JS::Rooted argumentsValue(cx);
- if (!mBuffer.read(cx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
+ JS::Rooted argumentsValue(aCx);
+ if (!mBuffer.read(aCx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
return;
}
MOZ_ASSERT(argumentsValue.isObject());
- JS::Rooted argumentsObj(cx, &argumentsValue.toObject());
- MOZ_ASSERT(JS_IsArrayObject(cx, argumentsObj));
+ JS::Rooted argumentsObj(aCx, &argumentsValue.toObject());
+ MOZ_ASSERT(JS_IsArrayObject(aCx, argumentsObj));
uint32_t length;
- if (!JS_GetArrayLength(cx, argumentsObj, &length)) {
+ if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
return;
}
Sequence arguments;
for (uint32_t i = 0; i < length; ++i) {
- JS::Rooted value(cx);
+ JS::Rooted value(aCx);
- if (!JS_GetElement(cx, argumentsObj, i, &value)) {
+ if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
return;
}
arguments.AppendElement(value);
}
- console->ProfileMethod(cx, mAction, arguments);
+ mConsole->ProfileMethod(aCx, mAction, arguments);
}
-private:
nsString mAction;
Sequence mArguments;
@@ -497,37 +590,22 @@
NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
+// We don't need to traverse/unlink mStorage and mSanbox because they are not
+// CCed objects and they are only used on the main thread, even when this
+// Console object is used on workers.
+
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mTimer)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mStorage)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-
- tmp->ClearConsoleData();
-
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorage)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-
- for (ConsoleCallData* data = tmp->mQueuedCalls.getFirst(); data != nullptr;
- data = data->getNext()) {
- if (data->mGlobal) {
- aCallbacks.Trace(&data->mGlobal, "data->mGlobal", aClosure);
- }
-
- for (uint32_t i = 0; i < data->mArguments.Length(); ++i) {
- aCallbacks.Trace(&data->mArguments[i], "data->mArguments[i]", aClosure);
- }
- }
-
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
@@ -535,9 +613,8 @@
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
Console::Console(nsPIDOMWindow* aWindow)
@@ -566,6 +643,23 @@
Console::~Console()
{
+ if (!NS_IsMainThread()) {
+ nsCOMPtr mainThread;
+ NS_GetMainThread(getter_AddRefs(mainThread));
+
+ if (mStorage) {
+ nsIConsoleAPIStorage* storage;
+ mStorage.forget(&storage);
+ NS_ProxyRelease(mainThread, storage, false);
+ }
+
+ if (mSandbox) {
+ nsIXPConnectJSObjectHolder* sandbox;
+ mSandbox.forget(&sandbox);
+ NS_ProxyRelease(mainThread, sandbox, false);
+ }
+ }
+
mozilla::DropJSObjects(this);
}
@@ -593,13 +687,7 @@
obs->RemoveObserver(this, "inner-window-destroyed");
}
- ClearConsoleData();
mTimerRegistry.Clear();
-
- if (mTimer) {
- mTimer->Cancel();
- mTimer = nullptr;
- }
}
return NS_OK;
@@ -685,7 +773,7 @@
if (!NS_IsMainThread()) {
// Here we are in a worker thread.
nsRefPtr runnable =
- new ConsoleProfileRunnable(aAction, aData);
+ new ConsoleProfileRunnable(this, aAction, aData);
runnable->Dispatch();
return;
}
@@ -843,43 +931,11 @@
const nsAString& aMethodString,
const Sequence& aData)
{
- // This RAII class removes the last element of the mQueuedCalls if something
- // goes wrong.
- class RAII {
- public:
- explicit RAII(LinkedList& aList)
- : mList(aList)
- , mUnfinished(true)
- {
- }
-
- ~RAII()
- {
- if (mUnfinished) {
- ConsoleCallData* data = mList.popLast();
- MOZ_ASSERT(data);
- delete data;
- }
- }
-
- void
- Finished()
- {
- mUnfinished = false;
- }
-
- private:
- LinkedList& mList;
- bool mUnfinished;
- };
-
- ConsoleCallData* callData = new ConsoleCallData();
- mQueuedCalls.insertBack(callData);
+ nsAutoPtr callData(new ConsoleCallData());
ClearException ce(aCx);
callData->Initialize(aCx, aMethodName, aMethodString, aData);
- RAII raii(mQueuedCalls);
if (mWindow) {
nsCOMPtr webNav = do_GetInterface(mWindow);
@@ -989,62 +1045,17 @@
}
}
- // The operation is completed. RAII class has to be disabled.
- raii.Finished();
-
- if (!NS_IsMainThread()) {
- // Here we are in a worker thread. The ConsoleCallData has to been removed
- // from the list and it will be deleted by the ConsoleCallDataRunnable or
- // by the Main-Thread Console object.
- mQueuedCalls.popLast();
-
- nsRefPtr runnable =
- new ConsoleCallDataRunnable(callData);
- runnable->Dispatch();
+ if (NS_IsMainThread()) {
+ callData->SetIDs(mOuterID, mInnerID);
+ ProcessCallData(callData);
return;
}
- if (!mTimer) {
- mTimer = do_CreateInstance("@mozilla.org/timer;1");
- mTimer->InitWithCallback(this, CALL_DELAY,
- nsITimer::TYPE_REPEATING_SLACK);
- }
-}
-
-void
-Console::AppendCallData(ConsoleCallData* aCallData)
-{
- mQueuedCalls.insertBack(aCallData);
-
- if (!mTimer) {
- mTimer = do_CreateInstance("@mozilla.org/timer;1");
- mTimer->InitWithCallback(this, CALL_DELAY,
- nsITimer::TYPE_REPEATING_SLACK);
- }
-}
-
-// Timer callback used to process each of the queued calls.
-NS_IMETHODIMP
-Console::Notify(nsITimer *timer)
-{
- MOZ_ASSERT(!mQueuedCalls.isEmpty());
-
- for (uint32_t i = 0; i < MESSAGES_IN_INTERVAL; ++i) {
- ConsoleCallData* data = mQueuedCalls.popFirst();
- if (!data) {
- break;
- }
-
- ProcessCallData(data);
- delete data;
- }
-
- if (mQueuedCalls.isEmpty() && mTimer) {
- mTimer->Cancel();
- mTimer = nullptr;
- }
-
- return NS_OK;
+ // Note: we can pass the reference of callData because this runnable calls
+ // ProcessCallData() synchronously.
+ nsRefPtr runnable =
+ new ConsoleCallDataRunnable(this, callData);
+ runnable->Dispatch();
}
// We store information to lazily compute the stack in the reserved slots of
@@ -1111,13 +1122,15 @@
event.mID.Construct();
event.mInnerID.Construct();
- if (mWindow) {
- event.mID.Value().SetAsUnsignedLong() = mOuterID;
- event.mInnerID.Value().SetAsUnsignedLong() = mInnerID;
+
+ MOZ_ASSERT(aData->mIDType != ConsoleCallData::eUnknown);
+ if (aData->mIDType == ConsoleCallData::eString) {
+ event.mID.Value().SetAsString() = aData->mOuterIDString;
+ event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
} else {
- // If we are in a JSM, the window doesn't exist.
- event.mID.Value().SetAsString() = NS_LITERAL_STRING("jsm");
- event.mInnerID.Value().SetAsString() = frame.mFilename;
+ MOZ_ASSERT(aData->mIDType == ConsoleCallData::eNumber);
+ event.mID.Value().SetAsUnsignedLong() = aData->mOuterIDNumber;
+ event.mInnerID.Value().SetAsUnsignedLong() = aData->mInnerIDNumber;
}
event.mLevel = aData->mMethodString;
@@ -1243,28 +1256,20 @@
return;
}
- nsAutoString innerID;
- innerID.AppendInt(mInnerID);
+ nsAutoString innerID, outerID;
- if (NS_FAILED(mStorage->RecordEvent(innerID, eventValue))) {
- NS_WARNING("Failed to record a console event.");
- }
-
- nsXPConnect* xpc = nsXPConnect::XPConnect();
- nsCOMPtr wrapper;
- const nsIID& iid = NS_GET_IID(nsISupports);
-
- if (NS_FAILED(xpc->WrapJS(cx, eventObj, iid, getter_AddRefs(wrapper)))) {
- return;
+ MOZ_ASSERT(aData->mIDType != ConsoleCallData::eUnknown);
+ if (aData->mIDType == ConsoleCallData::eString) {
+ outerID = aData->mOuterIDString;
+ innerID = aData->mInnerIDString;
+ } else {
+ MOZ_ASSERT(aData->mIDType == ConsoleCallData::eNumber);
+ outerID.AppendInt(aData->mOuterIDNumber);
+ innerID.AppendInt(aData->mInnerIDNumber);
}
- nsCOMPtr obs =
- do_GetService("@mozilla.org/observer-service;1");
- if (obs) {
- nsAutoString outerID;
- outerID.AppendInt(mOuterID);
-
- obs->NotifyObservers(wrapper, "console-api-log-event", outerID.get());
+ if (NS_FAILED(mStorage->RecordPendingEvent(innerID, outerID, eventValue))) {
+ NS_WARNING("Failed to record a console event.");
}
}
@@ -1689,14 +1694,6 @@
return value;
}
-void
-Console::ClearConsoleData()
-{
- while (ConsoleCallData* data = mQueuedCalls.popFirst()) {
- delete data;
- }
-}
-
bool
Console::ShouldIncludeStackTrace(MethodName aMethodName)
{
@@ -1711,5 +1708,24 @@
}
}
+nsIXPConnectJSObjectHolder*
+Console::GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mSandbox) {
+ nsIXPConnect* xpc = nsContentUtils::XPConnect();
+ MOZ_ASSERT(xpc, "This should never be null!");
+
+ nsresult rv = xpc->CreateSandbox(aCx, aPrincipal,
+ getter_AddRefs(mSandbox));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+ }
+
+ return mSandbox;
+}
+
} // namespace dom
} // namespace mozilla
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Console.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Console.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Console.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Console.h 2015-01-07 22:15:24.000000000 +0000
@@ -12,12 +12,12 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
-#include "nsITimer.h"
#include "nsWrapperCache.h"
#include "nsDOMNavigationTiming.h"
#include "nsPIDOMWindow.h"
class nsIConsoleAPIStorage;
+class nsIXPConnectJSObjectHolder;
namespace mozilla {
namespace dom {
@@ -25,17 +25,14 @@
class ConsoleCallData;
struct ConsoleStackEntry;
-class Console MOZ_FINAL : public nsITimerCallback
- , public nsIObserver
+class Console MOZ_FINAL : public nsIObserver
, public nsWrapperCache
{
~Console();
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console,
- nsITimerCallback)
- NS_DECL_NSITIMERCALLBACK
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Console)
NS_DECL_NSIOBSERVER
explicit Console(nsPIDOMWindow* aWindow);
@@ -132,9 +129,6 @@
const Sequence& aData);
void
- AppendCallData(ConsoleCallData* aData);
-
- void
ProcessCallData(ConsoleCallData* aData);
// If the first JS::Value of the array is a string, this method uses it to
@@ -191,17 +185,16 @@
IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
const nsTArray>& aArguments);
- void
- ClearConsoleData();
-
bool
ShouldIncludeStackTrace(MethodName aMethodName);
+ nsIXPConnectJSObjectHolder*
+ GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal);
+
nsCOMPtr mWindow;
- nsCOMPtr mTimer;
nsCOMPtr mStorage;
+ nsCOMPtr mSandbox;
- LinkedList mQueuedCalls;
nsDataHashtable mTimerRegistry;
nsDataHashtable mCounterRegistry;
@@ -209,6 +202,7 @@
uint64_t mInnerID;
friend class ConsoleCallData;
+ friend class ConsoleRunnable;
friend class ConsoleCallDataRunnable;
friend class ConsoleProfileRunnable;
};
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/DocumentFragment.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/DocumentFragment.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/DocumentFragment.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/DocumentFragment.h 2015-01-07 22:15:24.000000000 +0000
@@ -112,7 +112,7 @@
return;
}
- virtual Element* GetNameSpaceElement()
+ virtual Element* GetNameSpaceElement() MOZ_OVERRIDE
{
return nullptr;
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/DOMQuad.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/DOMQuad.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/DOMQuad.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/DOMQuad.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -84,25 +84,25 @@
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(QuadBounds, DOMRectReadOnly)
NS_DECL_ISUPPORTS_INHERITED
- virtual double X() const
+ virtual double X() const MOZ_OVERRIDE
{
double x1, x2;
GetHorizontalMinMax(&x1, &x2);
return x1;
}
- virtual double Y() const
+ virtual double Y() const MOZ_OVERRIDE
{
double y1, y2;
GetVerticalMinMax(&y1, &y2);
return y1;
}
- virtual double Width() const
+ virtual double Width() const MOZ_OVERRIDE
{
double x1, x2;
GetHorizontalMinMax(&x1, &x2);
return x2 - x1;
}
- virtual double Height() const
+ virtual double Height() const MOZ_OVERRIDE
{
double y1, y2;
GetVerticalMinMax(&y1, &y2);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/DOMStringList.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/DOMStringList.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/DOMStringList.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/DOMStringList.h 2015-01-07 22:15:24.000000000 +0000
@@ -24,7 +24,7 @@
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMStringList)
- virtual JSObject* WrapObject(JSContext* aCx);
+ virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
nsISupports* GetParentObject()
{
return nullptr;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Element.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Element.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Element.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Element.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -3078,8 +3078,10 @@
}
void
-Element::MozRequestFullScreen(const RequestFullscreenOptions& aOptions)
+Element::MozRequestFullScreen(JSContext* aCx, JS::Handle aOptions,
+ ErrorResult& aError)
{
+ MOZ_ASSERT_IF(!aCx, aOptions.isNullOrUndefined());
// Only grant full-screen requests if this is called from inside a trusted
// event handler (i.e. inside an event handler for a user initiated event).
// This stops the full-screen from being abused similar to the popups of old,
@@ -3103,13 +3105,23 @@
}
FullScreenOptions opts;
- if (aOptions.mVrDisplay) {
- opts.mVRHMDDevice = aOptions.mVrDisplay->GetHMD();
+ RequestFullscreenOptions fsOptions;
+
+ // We need to check if options is convertible to a dict first before
+ // trying to init fsOptions; otherwise Init() would throw, and we want to
+ // silently ignore non-dictionary values
+ if (aCx && IsConvertibleToDictionary(aCx, aOptions)) {
+ if (!fsOptions.Init(aCx, aOptions)) {
+ aError.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ if (fsOptions.mVrDisplay) {
+ opts.mVRHMDDevice = fsOptions.mVrDisplay->GetHMD();
+ }
}
OwnerDoc()->AsyncRequestFullScreen(this, opts);
-
- return;
}
void
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Element.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Element.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/Element.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/Element.h 2015-01-07 22:15:24.000000000 +0000
@@ -159,7 +159,7 @@
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID)
- NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) MOZ_OVERRIDE;
/**
* Method to get the full state of this element. See mozilla/EventStates.h
@@ -725,7 +725,10 @@
nsIPresShell::SetCapturingContent(nullptr, 0);
}
}
- void MozRequestFullScreen(const RequestFullscreenOptions& aOptions);
+
+ // aCx == nullptr is allowed only if aOptions.isNullOrUndefined()
+ void MozRequestFullScreen(JSContext* aCx, JS::Handle aOptions,
+ ErrorResult& aError);
void MozRequestPointerLock();
Attr* GetAttributeNode(const nsAString& aName);
already_AddRefed SetAttributeNode(Attr& aNewAttr,
@@ -1211,7 +1214,7 @@
nsIFrame* GetStyledFrame();
- virtual Element* GetNameSpaceElement()
+ virtual Element* GetNameSpaceElement() MOZ_OVERRIDE
{
return this;
}
@@ -1311,9 +1314,9 @@
NS_DECL_NSIDOMNODELIST
// nsINodeList
- virtual nsIContent* Item(uint32_t aIndex);
- virtual int32_t IndexOf(nsIContent* aContent);
- virtual nsINode* GetParentObject() { return mParent; }
+ virtual nsIContent* Item(uint32_t aIndex) MOZ_OVERRIDE;
+ virtual int32_t IndexOf(nsIContent* aContent) MOZ_OVERRIDE;
+ virtual nsINode* GetParentObject() MOZ_OVERRIDE { return mParent; }
virtual uint32_t Length() const;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
protected:
@@ -1467,43 +1470,45 @@
#define NS_FORWARD_NSIDOMELEMENT_TO_GENERIC \
typedef mozilla::dom::Element Element; \
-NS_IMETHOD GetTagName(nsAString& aTagName) MOZ_FINAL \
+NS_IMETHOD GetTagName(nsAString& aTagName) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::GetTagName(aTagName); \
return NS_OK; \
} \
-NS_IMETHOD GetId(nsAString& aId) MOZ_FINAL \
+NS_IMETHOD GetId(nsAString& aId) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::GetId(aId); \
return NS_OK; \
} \
-NS_IMETHOD SetId(const nsAString& aId) MOZ_FINAL \
+NS_IMETHOD SetId(const nsAString& aId) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::SetId(aId); \
return NS_OK; \
} \
-NS_IMETHOD GetClassName(nsAString& aClassName) MOZ_FINAL \
+NS_IMETHOD GetClassName(nsAString& aClassName) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::GetClassName(aClassName); \
return NS_OK; \
} \
-NS_IMETHOD SetClassName(const nsAString& aClassName) MOZ_FINAL \
+NS_IMETHOD SetClassName(const nsAString& aClassName) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::SetClassName(aClassName); \
return NS_OK; \
} \
-NS_IMETHOD GetClassList(nsISupports** aClassList) MOZ_FINAL \
+NS_IMETHOD GetClassList(nsISupports** aClassList) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::GetClassList(aClassList); \
return NS_OK; \
} \
NS_IMETHOD GetAttributes(nsIDOMMozNamedAttrMap** aAttributes) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
NS_ADDREF(*aAttributes = Attributes()); \
return NS_OK; \
} \
using Element::GetAttribute; \
NS_IMETHOD GetAttribute(const nsAString& name, nsAString& _retval) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
nsString attr; \
GetAttribute(name, attr); \
@@ -1512,13 +1517,13 @@
} \
NS_IMETHOD GetAttributeNS(const nsAString& namespaceURI, \
const nsAString& localName, \
- nsAString& _retval) MOZ_FINAL \
+ nsAString& _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::GetAttributeNS(namespaceURI, localName, _retval); \
return NS_OK; \
} \
NS_IMETHOD SetAttribute(const nsAString& name, \
- const nsAString& value) \
+ const nsAString& value) MOZ_OVERRIDE \
{ \
mozilla::ErrorResult rv; \
Element::SetAttribute(name, value, rv); \
@@ -1526,14 +1531,14 @@
} \
NS_IMETHOD SetAttributeNS(const nsAString& namespaceURI, \
const nsAString& qualifiedName, \
- const nsAString& value) MOZ_FINAL \
+ const nsAString& value) MOZ_FINAL MOZ_OVERRIDE \
{ \
mozilla::ErrorResult rv; \
Element::SetAttributeNS(namespaceURI, qualifiedName, value, rv); \
return rv.ErrorCode(); \
} \
using Element::RemoveAttribute; \
-NS_IMETHOD RemoveAttribute(const nsAString& name) MOZ_FINAL \
+NS_IMETHOD RemoveAttribute(const nsAString& name) MOZ_FINAL MOZ_OVERRIDE \
{ \
mozilla::ErrorResult rv; \
RemoveAttribute(name, rv); \
@@ -1541,6 +1546,7 @@
} \
NS_IMETHOD RemoveAttributeNS(const nsAString& namespaceURI, \
const nsAString& localName) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
mozilla::ErrorResult rv; \
Element::RemoveAttributeNS(namespaceURI, localName, rv); \
@@ -1548,31 +1554,31 @@
} \
using Element::HasAttribute; \
NS_IMETHOD HasAttribute(const nsAString& name, \
- bool* _retval) MOZ_FINAL \
+ bool* _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
*_retval = HasAttribute(name); \
return NS_OK; \
} \
NS_IMETHOD HasAttributeNS(const nsAString& namespaceURI, \
const nsAString& localName, \
- bool* _retval) MOZ_FINAL \
+ bool* _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
*_retval = Element::HasAttributeNS(namespaceURI, localName); \
return NS_OK; \
} \
-NS_IMETHOD HasAttributes(bool* _retval) MOZ_FINAL \
+NS_IMETHOD HasAttributes(bool* _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
*_retval = Element::HasAttributes(); \
return NS_OK; \
} \
NS_IMETHOD GetAttributeNode(const nsAString& name, \
- nsIDOMAttr** _retval) MOZ_FINAL \
+ nsIDOMAttr** _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
NS_IF_ADDREF(*_retval = Element::GetAttributeNode(name)); \
return NS_OK; \
} \
NS_IMETHOD SetAttributeNode(nsIDOMAttr* newAttr, \
- nsIDOMAttr** _retval) MOZ_FINAL \
+ nsIDOMAttr** _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
if (!newAttr) { \
return NS_ERROR_INVALID_POINTER; \
@@ -1583,7 +1589,7 @@
return rv.ErrorCode(); \
} \
NS_IMETHOD RemoveAttributeNode(nsIDOMAttr* oldAttr, \
- nsIDOMAttr** _retval) MOZ_FINAL \
+ nsIDOMAttr** _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
if (!oldAttr) { \
return NS_ERROR_INVALID_POINTER; \
@@ -1595,14 +1601,14 @@
} \
NS_IMETHOD GetAttributeNodeNS(const nsAString& namespaceURI, \
const nsAString& localName, \
- nsIDOMAttr** _retval) MOZ_FINAL \
+ nsIDOMAttr** _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
NS_IF_ADDREF(*_retval = Element::GetAttributeNodeNS(namespaceURI, \
localName)); \
return NS_OK; \
} \
NS_IMETHOD SetAttributeNodeNS(nsIDOMAttr* newAttr, \
- nsIDOMAttr** _retval) MOZ_FINAL \
+ nsIDOMAttr** _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
mozilla::ErrorResult rv; \
mozilla::dom::Attr* attr = static_cast(newAttr); \
@@ -1611,6 +1617,7 @@
} \
NS_IMETHOD GetElementsByTagName(const nsAString& name, \
nsIDOMHTMLCollection** _retval) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
Element::GetElementsByTagName(name, _retval); \
return NS_OK; \
@@ -1618,21 +1625,25 @@
NS_IMETHOD GetElementsByTagNameNS(const nsAString& namespaceURI, \
const nsAString& localName, \
nsIDOMHTMLCollection** _retval) MOZ_FINAL \
+ MOZ_OVERRIDE\
{ \
return Element::GetElementsByTagNameNS(namespaceURI, localName, \
_retval); \
} \
NS_IMETHOD GetElementsByClassName(const nsAString& classes, \
nsIDOMHTMLCollection** _retval) MOZ_FINAL \
+ MOZ_OVERRIDE\
{ \
return Element::GetElementsByClassName(classes, _retval); \
} \
NS_IMETHOD GetChildElements(nsIDOMNodeList** aChildElements) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
nsIHTMLCollection* list = FragmentOrElement::Children(); \
return CallQueryInterface(list, aChildElements); \
} \
NS_IMETHOD GetFirstElementChild(nsIDOMElement** aFirstElementChild) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
Element* element = Element::GetFirstElementChild(); \
if (!element) { \
@@ -1642,6 +1653,7 @@
return CallQueryInterface(element, aFirstElementChild); \
} \
NS_IMETHOD GetLastElementChild(nsIDOMElement** aLastElementChild) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
Element* element = Element::GetLastElementChild(); \
if (!element) { \
@@ -1651,7 +1663,7 @@
return CallQueryInterface(element, aLastElementChild); \
} \
NS_IMETHOD GetPreviousElementSibling(nsIDOMElement** aPreviousElementSibling) \
- MOZ_FINAL \
+ MOZ_FINAL MOZ_OVERRIDE \
{ \
Element* element = Element::GetPreviousElementSibling(); \
if (!element) { \
@@ -1661,7 +1673,7 @@
return CallQueryInterface(element, aPreviousElementSibling); \
} \
NS_IMETHOD GetNextElementSibling(nsIDOMElement** aNextElementSibling) \
- MOZ_FINAL \
+ MOZ_FINAL MOZ_OVERRIDE \
{ \
Element* element = Element::GetNextElementSibling(); \
if (!element) { \
@@ -1671,121 +1683,125 @@
return CallQueryInterface(element, aNextElementSibling); \
} \
NS_IMETHOD GetChildElementCount(uint32_t* aChildElementCount) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
*aChildElementCount = Element::ChildElementCount(); \
return NS_OK; \
} \
-NS_IMETHOD MozRemove() MOZ_FINAL \
+NS_IMETHOD MozRemove() MOZ_FINAL MOZ_OVERRIDE \
{ \
nsINode::Remove(); \
return NS_OK; \
} \
NS_IMETHOD GetClientRects(nsIDOMClientRectList** _retval) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
*_retval = Element::GetClientRects().take(); \
return NS_OK; \
} \
NS_IMETHOD GetBoundingClientRect(nsIDOMClientRect** _retval) MOZ_FINAL \
+ MOZ_OVERRIDE \
{ \
*_retval = Element::GetBoundingClientRect().take(); \
return NS_OK; \
} \
-NS_IMETHOD GetScrollTop(int32_t* aScrollTop) MOZ_FINAL \
+NS_IMETHOD GetScrollTop(int32_t* aScrollTop) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aScrollTop = Element::ScrollTop(); \
return NS_OK; \
} \
-NS_IMETHOD SetScrollTop(int32_t aScrollTop) MOZ_FINAL \
+NS_IMETHOD SetScrollTop(int32_t aScrollTop) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::SetScrollTop(aScrollTop); \
return NS_OK; \
} \
-NS_IMETHOD GetScrollLeft(int32_t* aScrollLeft) MOZ_FINAL \
+NS_IMETHOD GetScrollLeft(int32_t* aScrollLeft) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aScrollLeft = Element::ScrollLeft(); \
return NS_OK; \
} \
-NS_IMETHOD SetScrollLeft(int32_t aScrollLeft) MOZ_FINAL \
+NS_IMETHOD SetScrollLeft(int32_t aScrollLeft) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::SetScrollLeft(aScrollLeft); \
return NS_OK; \
} \
-NS_IMETHOD GetScrollWidth(int32_t* aScrollWidth) MOZ_FINAL \
+NS_IMETHOD GetScrollWidth(int32_t* aScrollWidth) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aScrollWidth = Element::ScrollWidth(); \
return NS_OK; \
} \
-NS_IMETHOD GetScrollHeight(int32_t* aScrollHeight) MOZ_FINAL \
+NS_IMETHOD GetScrollHeight(int32_t* aScrollHeight) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aScrollHeight = Element::ScrollHeight(); \
return NS_OK; \
} \
-NS_IMETHOD GetClientTop(int32_t* aClientTop) MOZ_FINAL \
+NS_IMETHOD GetClientTop(int32_t* aClientTop) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aClientTop = Element::ClientTop(); \
return NS_OK; \
} \
-NS_IMETHOD GetClientLeft(int32_t* aClientLeft) MOZ_FINAL \
+NS_IMETHOD GetClientLeft(int32_t* aClientLeft) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aClientLeft = Element::ClientLeft(); \
return NS_OK; \
} \
-NS_IMETHOD GetClientWidth(int32_t* aClientWidth) MOZ_FINAL \
+NS_IMETHOD GetClientWidth(int32_t* aClientWidth) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aClientWidth = Element::ClientWidth(); \
return NS_OK; \
} \
-NS_IMETHOD GetClientHeight(int32_t* aClientHeight) MOZ_FINAL \
+NS_IMETHOD GetClientHeight(int32_t* aClientHeight) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aClientHeight = Element::ClientHeight(); \
return NS_OK; \
} \
-NS_IMETHOD GetScrollLeftMax(int32_t* aScrollLeftMax) MOZ_FINAL \
+NS_IMETHOD GetScrollLeftMax(int32_t* aScrollLeftMax) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aScrollLeftMax = Element::ScrollLeftMax(); \
return NS_OK; \
} \
-NS_IMETHOD GetScrollTopMax(int32_t* aScrollTopMax) MOZ_FINAL \
+NS_IMETHOD GetScrollTopMax(int32_t* aScrollTopMax) MOZ_FINAL MOZ_OVERRIDE \
{ \
*aScrollTopMax = Element::ScrollTopMax(); \
return NS_OK; \
} \
NS_IMETHOD MozMatchesSelector(const nsAString& selector, \
- bool* _retval) MOZ_FINAL \
+ bool* _retval) MOZ_FINAL MOZ_OVERRIDE \
{ \
mozilla::ErrorResult rv; \
*_retval = Element::MozMatchesSelector(selector, rv); \
return rv.ErrorCode(); \
} \
-NS_IMETHOD SetCapture(bool retargetToElement) MOZ_FINAL \
+NS_IMETHOD SetCapture(bool retargetToElement) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::SetCapture(retargetToElement); \
return NS_OK; \
} \
-NS_IMETHOD ReleaseCapture(void) MOZ_FINAL \
+NS_IMETHOD ReleaseCapture(void) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::ReleaseCapture(); \
return NS_OK; \
} \
-NS_IMETHOD MozRequestFullScreen(void) MOZ_FINAL \
+NS_IMETHOD MozRequestFullScreen(void) MOZ_FINAL MOZ_OVERRIDE \
{ \
- Element::MozRequestFullScreen(mozilla::dom::RequestFullscreenOptions()); \
- return NS_OK; \
+ mozilla::ErrorResult rv; \
+ Element::MozRequestFullScreen(nullptr, JS::UndefinedHandleValue, rv); \
+ return rv.ErrorCode(); \
} \
-NS_IMETHOD MozRequestPointerLock(void) MOZ_FINAL \
+NS_IMETHOD MozRequestPointerLock(void) MOZ_FINAL MOZ_OVERRIDE \
{ \
Element::MozRequestPointerLock(); \
return NS_OK; \
} \
using nsINode::QuerySelector; \
NS_IMETHOD QuerySelector(const nsAString& aSelector, \
- nsIDOMElement **aReturn) MOZ_FINAL \
+ nsIDOMElement **aReturn) MOZ_FINAL MOZ_OVERRIDE \
{ \
return nsINode::QuerySelector(aSelector, aReturn); \
} \
using nsINode::QuerySelectorAll; \
NS_IMETHOD QuerySelectorAll(const nsAString& aSelector, \
- nsIDOMNodeList **aReturn) MOZ_FINAL \
+ nsIDOMNodeList **aReturn) MOZ_FINAL MOZ_OVERRIDE \
{ \
return nsINode::QuerySelectorAll(aSelector, aReturn); \
}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/EventSource.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/EventSource.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/EventSource.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/EventSource.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -201,11 +201,7 @@
// The conditional here is historical and not necessarily sane.
if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
- const char *filename;
- if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) {
- mScriptFile.AssignASCII(filename);
- }
-
+ nsJSUtils::GetCallingLocation(cx, mScriptFile, &mScriptLine);
mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
}
@@ -504,7 +500,7 @@
NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
// nsIAsyncVerifyRedirectCallback implementation
- NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult)
+ NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult) MOZ_OVERRIDE
{
nsresult rv = mEventSource->OnRedirectVerifyCallback(aResult);
if (NS_FAILED(rv)) {
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/File.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/File.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/File.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/File.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -1083,7 +1083,7 @@
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
- nsISupports *aClosure, bool aAnonymize)
+ nsISupports *aClosure, bool aAnonymize) MOZ_OVERRIDE
{
typedef FileImplMemory::DataOwner DataOwner;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/File.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/File.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/File.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/File.h 2015-01-07 22:15:24.000000000 +0000
@@ -439,7 +439,7 @@
return mIsFile && mLastModificationDate == UINT64_MAX;
}
- virtual bool IsFile() const
+ virtual bool IsFile() const MOZ_OVERRIDE
{
return mIsFile;
}
@@ -460,13 +460,13 @@
return false;
}
- virtual bool IsSizeUnknown() const
+ virtual bool IsSizeUnknown() const MOZ_OVERRIDE
{
return mLength == UINT64_MAX;
}
- virtual void Unlink() {}
- virtual void Traverse(nsCycleCollectionTraversalCallback &aCb) {}
+ virtual void Unlink() MOZ_OVERRIDE {}
+ virtual void Traverse(nsCycleCollectionTraversalCallback &aCb) MOZ_OVERRIDE {}
protected:
virtual ~FileImplBase() {}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/FragmentOrElement.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/FragmentOrElement.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/FragmentOrElement.h 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/FragmentOrElement.h 2015-01-07 22:15:24.000000000 +0000
@@ -99,7 +99,7 @@
// nsIWeakReference
NS_DECL_NSIWEAKREFERENCE
- virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
void NoticeNodeDestruction()
{
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/ImageEncoder.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/ImageEncoder.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/ImageEncoder.cpp 2015-01-06 13:14:30.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/ImageEncoder.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -82,7 +82,7 @@
, mFailed(false)
{}
- NS_IMETHOD Run()
+ NS_IMETHOD Run() MOZ_OVERRIDE
{
nsresult rv = NS_OK;
MOZ_ASSERT(NS_IsMainThread());
@@ -190,7 +190,7 @@
return rv;
}
- NS_IMETHOD Run()
+ NS_IMETHOD Run() MOZ_OVERRIDE
{
uint64_t imgSize;
void* imgData = nullptr;
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsContentIterator.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsContentIterator.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsContentIterator.cpp 2015-01-06 13:14:31.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsContentIterator.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -86,23 +86,23 @@
// nsIContentIterator interface methods ------------------------------
- virtual nsresult Init(nsINode* aRoot);
+ virtual nsresult Init(nsINode* aRoot) MOZ_OVERRIDE;
- virtual nsresult Init(nsIDOMRange* aRange);
+ virtual nsresult Init(nsIDOMRange* aRange) MOZ_OVERRIDE;
- virtual void First();
+ virtual void First() MOZ_OVERRIDE;
- virtual void Last();
+ virtual void Last() MOZ_OVERRIDE;
- virtual void Next();
+ virtual void Next() MOZ_OVERRIDE;
- virtual void Prev();
+ virtual void Prev() MOZ_OVERRIDE;
- virtual nsINode* GetCurrentNode();
+ virtual nsINode* GetCurrentNode() MOZ_OVERRIDE;
- virtual bool IsDone();
+ virtual bool IsDone() MOZ_OVERRIDE;
- virtual nsresult PositionAt(nsINode* aCurNode);
+ virtual nsresult PositionAt(nsINode* aCurNode) MOZ_OVERRIDE;
protected:
virtual ~nsContentIterator();
@@ -1113,21 +1113,21 @@
// nsContentIterator overrides ------------------------------
- virtual nsresult Init(nsINode* aRoot);
+ virtual nsresult Init(nsINode* aRoot) MOZ_OVERRIDE;
- virtual nsresult Init(nsIDOMRange* aRange);
+ virtual nsresult Init(nsIDOMRange* aRange) MOZ_OVERRIDE;
- virtual void Next();
+ virtual void Next() MOZ_OVERRIDE;
- virtual void Prev();
+ virtual void Prev() MOZ_OVERRIDE;
- virtual nsresult PositionAt(nsINode* aCurNode);
+ virtual nsresult PositionAt(nsINode* aCurNode) MOZ_OVERRIDE;
// Must override these because we don't do PositionAt
- virtual void First();
+ virtual void First() MOZ_OVERRIDE;
// Must override these because we don't do PositionAt
- virtual void Last();
+ virtual void Last() MOZ_OVERRIDE;
protected:
virtual ~nsContentSubtreeIterator() {}
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsContentUtils.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsContentUtils.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsContentUtils.cpp 2015-01-06 13:14:31.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsContentUtils.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -339,7 +339,7 @@
NS_DECL_ISUPPORTS
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize)
+ nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
{
// We don't measure the |EventListenerManager| objects pointed to by the
// entries because those references are non-owning.
@@ -411,7 +411,7 @@
public:
NS_DECL_ISUPPORTS
- NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf)
+ NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf) MOZ_OVERRIDE
{
mCharset = aCharset;
return NS_OK;
@@ -3364,12 +3364,7 @@
if (!aLineNumber) {
JSContext *cx = GetCurrentJSContext();
if (cx) {
- const char* filename;
- uint32_t lineno;
- if (nsJSUtils::GetCallingLocation(cx, &filename, &lineno)) {
- spec = filename;
- aLineNumber = lineno;
- }
+ nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber);
}
}
if (spec.IsEmpty() && aURI)
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsDocument.cpp thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsDocument.cpp
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsDocument.cpp 2015-01-06 13:14:31.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsDocument.cpp 2015-01-07 22:15:24.000000000 +0000
@@ -9697,7 +9697,7 @@
~StubCSSLoaderObserver() {}
public:
NS_IMETHOD
- StyleSheetLoaded(CSSStyleSheet*, bool, nsresult)
+ StyleSheetLoaded(CSSStyleSheet*, bool, nsresult) MOZ_OVERRIDE
{
return NS_OK;
}
@@ -11837,7 +11837,7 @@
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICONTENTPERMISSIONREQUEST
- NS_IMETHOD Run()
+ NS_IMETHOD Run() MOZ_OVERRIDE
{
nsCOMPtr e = do_QueryReferent(mElement);
nsCOMPtr d = do_QueryReferent(mDocument);
diff -Nru thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsDocument.h thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsDocument.h
--- thunderbird-trunk-37.0~a1~hg20150106r17307.222089/mozilla/dom/base/nsDocument.h 2015-01-06 13:14:31.000000000 +0000
+++ thunderbird-trunk-37.0~a1~hg20150107r17323.222415/mozilla/dom/base/nsDocument.h 2015-01-07 22:15:24.000000000 +0000
@@ -991,23 +991,23 @@
int32_t aNamespaceID,
nsIContent **aResult) MOZ_OVERRIDE;
- virtual void Sanitize();
+ virtual void Sanitize() MOZ_OVERRIDE;
virtual void EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
- void *aData);
+ void *aData) MOZ_OVERRIDE;
- virtual bool CanSavePresentation(nsIRequest *aNewRequest);
- virtual void Destroy();
- virtual void RemovedFromDocShell();
- virtual already_AddRefed GetLayoutHistoryState() const;
+ virtual bool CanSavePresentation(nsIRequest *aNewRequest) MOZ_OVERRIDE;
+ virtual void Destroy() MOZ_OVERRIDE;
+ virtual void RemovedFromDocShell() MOZ_OVERRIDE;
+ virtual already_AddRefed GetLayoutHistoryState() const MOZ_OVERRIDE;
- virtual void BlockOnload();
- virtual void UnblockOnload(bool aFireSync);
+ virtual void BlockOnload() MOZ_OVERRIDE;
+ virtual void UnblockOnload(bool aFireSync) MOZ_OVERRIDE;
- virtual void AddStyleRelevantLink(mozilla::dom::Link* aLink);
- virtual void ForgetLink(mozilla::dom::Link* aLink);
+ virtual void AddStyleRelevantLink(mozilla::dom::Link* aLink) MOZ_OVERRIDE;
+ virtual void ForgetLink(mozilla::dom::Link* aLink) MOZ_OVERRIDE;
- void ClearBoxObjectFor(nsIContent* aContent);
+ virtual void ClearBoxObjectFor(nsIContent* aContent) MOZ_OVERRIDE;
virtual already_AddRefed
GetBoxObjectFor(mozilla::dom::Element* aElement,
@@ -1016,31 +1016,31 @@
virtual Element*
GetAnonymousElementByAttribute(nsIContent* aElement,
nsIAtom* aAttrName,
- const nsAString& aAttrValue) const;
+ const nsAString& aAttrValue) const MOZ_OVERRIDE;
virtual Element* ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
- bool aFlushLayout);
+ bool aFlushLayout) MOZ_OVERRIDE;
virtual nsresult NodesFromRectHelper(float aX, float aY,
float aTopSize, float aRightSize,
float aBottomSize, float aLeftSize,
bool aIgnoreRootScrollFrame,
bool aFlushLayout,
- nsIDOMNodeList** aReturn);
+ nsIDOMNodeList** aReturn) MOZ_OVERRIDE;
- virtual void FlushSkinBindings();
+ virtual void FlushSkinBindings() MOZ_OVERRIDE;
- virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader);
- virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader);
- virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell);
- virtual bool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell);
+ virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) MOZ_OVERRIDE;
+ virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) MOZ_OVERRIDE;
+ virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) MOZ_OVERRIDE;
+ virtual bool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) MOZ_OVERRIDE;
virtual nsIDocument*
RequestExternalResource(nsIURI* aURI,
nsINode* aRequestingNode,
- ExternalResourceLoad** aPendingLoad);
+ ExternalResourceLoad** aPendingLoad) MOZ_OVERRIDE;
virtual void
- EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData);
+ EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData) MOZ_OVERRIDE;
nsTArray mHostObjectURIs;
@@ -1049,7 +1049,7 @@
nsSMILAnimationController* GetAnimationController() MOZ_OVERRIDE;
virtual mozilla::PendingPlayerTracker*
- GetPendingPlayerTracker() MOZ_FINAL
+ GetPendingPlayerTracker() MOZ_FINAL MOZ_OVERRIDE
{
return mPendingPlayerTracker;
}
@@ -1135,9 +1135,9 @@
virtual void MozSetImageElement(const nsAString& aImageElementId,
Element* aElement) MOZ_OVERRIDE;
- virtual nsresult AddImage(imgIRequest* aImage);
- virtual nsresult RemoveImage(imgIRequest* aImage, uint32_t aFlags);
- virtual nsresult SetImageLockingState(bool aLocked);
+ virtual nsresult AddImage(imgIRequest* aImage) MOZ_OVERRIDE;
+ virtual nsresult RemoveImage(imgIRequest* aImage, uint32_t aFlags) MOZ_OVERRIDE;
+ virtual nsresult SetImageLockingState(bool aLocked) MOZ_OVERRIDE;
// AddPlugin adds a plugin-related element to mPlugins when the element is
// added to the tree.
@@ -1312,25 +1312,25 @@
mozilla::ErrorResult& rv) MOZ_OVERRIDE;
virtual void UseRegistryFromDocument(nsIDocument* aDocument) MOZ_OVERRIDE;
- virtual nsIDocument* MasterDocument()
+ virtual nsIDocument* MasterDocument() MOZ_OVERRIDE
{
return mMasterDocument ? mMasterDocument.get()
: this;
}
- virtual void SetMasterDocument(nsIDocument* master)
+ virtual void SetMasterDocument(nsIDocument* master) MOZ_OVERRIDE
{
MOZ_ASSERT(master);
mMasterDocument = master;
UseRegistryFromDocument(mMasterDocument);
}
- virtual bool IsMasterDocument()
+ virtual bool IsMasterDocument() MOZ_OVERRIDE
{
return !mMasterDocument;
}
- virtual mozilla::dom::ImportManager* ImportManager()
+ virtual mozilla::dom::ImportManager* ImportManager() MOZ_OVERRIDE
{
if (mImportManager) {
MOZ_ASSERT(!mMasterDocument, "Only the master document has ImportManager set");
@@ -1349,22 +1349,22 @@
return mImportManager.get();
}
- virtual bool HasSubImportLink(nsINode* aLink)
+ virtual bool HasSubImportLink(nsINode* aLink) MOZ_OVERRIDE
{
return mSubImportLinks.Contains(aLink);
}
- virtual uint32_t IndexOfSubImportLink(nsINode* aLink)
+ virtual uint32_t IndexOfSubImportLink(nsINode* aLink) MOZ_OVERRIDE
{
return mSubImportLinks.IndexOf(aLink);
}
- virtual void AddSubImportLink(nsINode* aLink)
+ virtual void AddSubImportLink(nsINode* aLink) MOZ_OVERRIDE
{
mSubImportLinks.AppendElement(aLink);
}
- virtual nsINode* GetSubImportLink(uint32_t aIdx)
+ virtual nsINode* GetSubImportLink(uint32_t aIdx) MOZ_OVERRIDE
{
return aIdx < mSubImportLinks.Length() ? mSubImportLinks[aIdx].get()
: nullptr;
@@ -1562,7 +1562,7 @@
// ex. ,