Merge m-c to elm
7
CLOBBER
@ -18,7 +18,8 @@
|
||||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
||||
#
|
||||
|
||||
Bug 934646 needs a clobber -- the icon resources previously copied
|
||||
into $OBJDIR/mobile/android/base/res will conflict with those in
|
||||
$BRANDING_DIRECTORY/res.
|
||||
# Are you updating CLOBBER because you think it's needed for your WebIDL
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 932982 apparently requires a clobber or else we get B2G mochitest-2 failures.
|
||||
|
@ -692,7 +692,7 @@ getRoleCB(AtkObject *aAtkObj)
|
||||
return aAtkObj->role;
|
||||
}
|
||||
|
||||
AtkAttributeSet*
|
||||
static AtkAttributeSet*
|
||||
ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes)
|
||||
{
|
||||
if (!aAttributes)
|
||||
|
@ -12,12 +12,87 @@
|
||||
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "nsIPersistentProperties2.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
AtkAttributeSet* ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes);
|
||||
static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED];
|
||||
|
||||
static AtkAttributeSet*
|
||||
ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes)
|
||||
{
|
||||
if (!aAttributes)
|
||||
return nullptr;
|
||||
|
||||
AtkAttributeSet* objAttributeSet = nullptr;
|
||||
nsCOMPtr<nsISimpleEnumerator> propEnum;
|
||||
nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
bool hasMore = false;
|
||||
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> sup;
|
||||
rv = propEnum->GetNext(getter_AddRefs(sup));
|
||||
NS_ENSURE_SUCCESS(rv, objAttributeSet);
|
||||
|
||||
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
|
||||
NS_ENSURE_TRUE(propElem, objAttributeSet);
|
||||
|
||||
nsAutoCString name;
|
||||
rv = propElem->GetKey(name);
|
||||
NS_ENSURE_SUCCESS(rv, objAttributeSet);
|
||||
|
||||
nsAutoString value;
|
||||
rv = propElem->GetValue(value);
|
||||
NS_ENSURE_SUCCESS(rv, objAttributeSet);
|
||||
|
||||
AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
|
||||
objAttr->name = g_strdup(name.get());
|
||||
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
|
||||
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
|
||||
|
||||
// Handle attributes where atk has its own name.
|
||||
const char* atkName = nullptr;
|
||||
nsAutoString atkValue;
|
||||
if (name.EqualsLiteral("color")) {
|
||||
// The format of the atk attribute is r,g,b and the gecko one is
|
||||
// rgb(r,g,b).
|
||||
atkValue = Substring(value, 5, value.Length() - 1);
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR];
|
||||
} else if (name.EqualsLiteral("background-color")) {
|
||||
// The format of the atk attribute is r,g,b and the gecko one is
|
||||
// rgb(r,g,b).
|
||||
atkValue = Substring(value, 5, value.Length() - 1);
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR];
|
||||
} else if (name.EqualsLiteral("font-family")) {
|
||||
atkValue = value;
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME];
|
||||
} else if (name.Equals("font-size")) {
|
||||
// ATK wants the number of pixels without px at the end.
|
||||
atkValue = StringHead(value, value.Length() - 2);
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE];
|
||||
} else if (name.EqualsLiteral("font-weight")) {
|
||||
atkValue = value;
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT];
|
||||
} else if (name.EqualsLiteral("invalid")) {
|
||||
atkValue = value;
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID];
|
||||
}
|
||||
|
||||
if (atkName) {
|
||||
objAttr = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
|
||||
objAttr->name = g_strdup(atkName);
|
||||
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get());
|
||||
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
|
||||
}
|
||||
}
|
||||
|
||||
// libatk-adaptor will free it
|
||||
return objAttributeSet;
|
||||
}
|
||||
|
||||
static void
|
||||
ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString)
|
||||
@ -188,7 +263,7 @@ getRunAttributesCB(AtkText *aText, gint aOffset,
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
return ConvertToAtkAttributeSet(attributes);
|
||||
return ConvertToAtkTextAttributeSet(attributes);
|
||||
}
|
||||
|
||||
static AtkAttributeSet*
|
||||
@ -203,7 +278,7 @@ getDefaultAttributesCB(AtkText *aText)
|
||||
return nullptr;
|
||||
|
||||
nsCOMPtr<nsIPersistentProperties> attributes = text->DefaultTextAttributes();
|
||||
return ConvertToAtkAttributeSet(attributes);
|
||||
return ConvertToAtkTextAttributeSet(attributes);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -415,4 +490,9 @@ textInterfaceInitCB(AtkTextIface* aIface)
|
||||
aIface->remove_selection = removeTextSelectionCB;
|
||||
aIface->set_selection = setTextSelectionCB;
|
||||
aIface->set_caret_offset = setCaretOffsetCB;
|
||||
|
||||
// Cache the string values of the atk text attribute names.
|
||||
for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++)
|
||||
sAtkTextAttrNames[i] =
|
||||
atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i));
|
||||
}
|
||||
|
@ -548,14 +548,34 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
|
||||
int32_t aEndHTOffset,
|
||||
nsRange* aRange)
|
||||
bool
|
||||
HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
|
||||
nsRange* aRange)
|
||||
{
|
||||
// If the given offsets are 0 and associated editor is empty then return
|
||||
// collapsed range with editor root element as range container.
|
||||
if (aStartHTOffset == 0 && aEndHTOffset == 0) {
|
||||
DOMPoint startPoint = OffsetToDOMPoint(aStartOffset);
|
||||
if (!startPoint.node)
|
||||
return false;
|
||||
|
||||
aRange->SetStart(startPoint.node, startPoint.idx);
|
||||
if (aStartOffset == aEndOffset) {
|
||||
aRange->SetEnd(startPoint.node, startPoint.idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
DOMPoint endPoint = OffsetToDOMPoint(aEndOffset);
|
||||
if (!endPoint.node)
|
||||
return false;
|
||||
|
||||
aRange->SetEnd(endPoint.node, endPoint.idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
DOMPoint
|
||||
HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset)
|
||||
{
|
||||
// 0 offset is valid even if no children. In this case the associated editor
|
||||
// is empty so return a DOM point for editor root element.
|
||||
if (aOffset == 0) {
|
||||
nsCOMPtr<nsIEditor> editor = GetEditor();
|
||||
if (editor) {
|
||||
bool isEmpty = false;
|
||||
@ -565,40 +585,36 @@ HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
|
||||
editor->GetRootElement(getter_AddRefs(editorRootElm));
|
||||
|
||||
nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm));
|
||||
if (editorRoot) {
|
||||
aRange->SetStart(editorRoot, 0);
|
||||
aRange->SetEnd(editorRoot, 0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
return DOMPoint(editorRoot, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Accessible> startAcc, endAcc;
|
||||
int32_t startOffset = aStartHTOffset, endOffset = aEndHTOffset;
|
||||
nsIFrame *startFrame = nullptr, *endFrame = nullptr;
|
||||
int32_t childIdx = GetChildIndexAtOffset(aOffset);
|
||||
if (childIdx == -1)
|
||||
return DOMPoint();
|
||||
|
||||
startFrame = GetPosAndText(startOffset, endOffset, nullptr, &endFrame,
|
||||
getter_AddRefs(startAcc), getter_AddRefs(endAcc));
|
||||
if (!startAcc || !endAcc)
|
||||
return NS_ERROR_FAILURE;
|
||||
Accessible* child = GetChildAt(childIdx);
|
||||
int32_t innerOffset = aOffset - GetChildOffset(childIdx);
|
||||
|
||||
DOMPoint startPoint, endPoint;
|
||||
nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc,
|
||||
&startPoint);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// A text leaf case. The point is inside the text node.
|
||||
if (child->IsTextLeaf()) {
|
||||
nsIContent* content = child->GetContent();
|
||||
int32_t idx = 0;
|
||||
if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
|
||||
innerOffset, &idx)))
|
||||
return DOMPoint();
|
||||
|
||||
rv = aRange->SetStart(startPoint.node, startPoint.idx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return DOMPoint(content, idx);
|
||||
}
|
||||
|
||||
if (aStartHTOffset == aEndHTOffset)
|
||||
return aRange->SetEnd(startPoint.node, startPoint.idx);
|
||||
|
||||
rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc, &endPoint);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return aRange->SetEnd(endPoint.node, endPoint.idx);
|
||||
// Case of embedded object. The point is either before or after the element.
|
||||
NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
|
||||
nsINode* node = child->GetNode();
|
||||
nsINode* parentNode = node->GetParentNode();
|
||||
return parentNode ?
|
||||
DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
|
||||
DOMPoint();
|
||||
}
|
||||
|
||||
int32_t
|
||||
@ -1580,7 +1596,8 @@ HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
|
||||
if (!range)
|
||||
return false;
|
||||
|
||||
HypertextOffsetsToDOMRange(startOffset, endOffset, range);
|
||||
if (!OffsetsToDOMRange(startOffset, endOffset, range))
|
||||
return false;
|
||||
|
||||
// If new range was created then add it, otherwise notify selection listeners
|
||||
// that existing selection range was changed.
|
||||
@ -1610,8 +1627,7 @@ HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
|
||||
uint32_t aScrollType)
|
||||
{
|
||||
nsRefPtr<nsRange> range = new nsRange(mContent);
|
||||
nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
if (OffsetsToDOMRange(aStartOffset, aEndOffset, range))
|
||||
nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType);
|
||||
}
|
||||
|
||||
@ -1629,8 +1645,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
|
||||
this);
|
||||
|
||||
nsRefPtr<nsRange> range = new nsRange(mContent);
|
||||
nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range);
|
||||
if (NS_FAILED(rv))
|
||||
if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range))
|
||||
return;
|
||||
|
||||
nsPresContext* presContext = frame->PresContext();
|
||||
@ -1658,7 +1673,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
|
||||
int16_t hPercent = offsetPointX * 100 / size.width;
|
||||
int16_t vPercent = offsetPointY * 100 / size.height;
|
||||
|
||||
rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent);
|
||||
nsresult rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
|
@ -17,6 +17,9 @@ namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
struct DOMPoint {
|
||||
DOMPoint() : node(nullptr), idx(0) { }
|
||||
DOMPoint(nsINode* aNode, int32_t aIdx) : node(aNode), idx(aIdx) { }
|
||||
|
||||
nsINode* node;
|
||||
int32_t idx;
|
||||
};
|
||||
@ -128,15 +131,24 @@ public:
|
||||
bool aIsEndOffset = false) const;
|
||||
|
||||
/**
|
||||
* Turn a start and end hypertext offsets into DOM range.
|
||||
* Convert start and end hypertext offsets into DOM range.
|
||||
*
|
||||
* @param aStartHTOffset [in] the given start hypertext offset
|
||||
* @param aEndHTOffset [in] the given end hypertext offset
|
||||
* @param aRange [out] the range whose bounds to set
|
||||
* @param aStartOffset [in] the given start hypertext offset
|
||||
* @param aEndOffset [in] the given end hypertext offset
|
||||
* @param aRange [in, out] the range whose bounds to set
|
||||
* @return true if conversion was successful
|
||||
*/
|
||||
nsresult HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
|
||||
int32_t aEndHTOffset,
|
||||
nsRange* aRange);
|
||||
bool OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
|
||||
nsRange* aRange);
|
||||
|
||||
/**
|
||||
* Convert the given offset into DOM point.
|
||||
*
|
||||
* If offset is at text leaf then DOM point is (text node, offsetInTextNode),
|
||||
* if before embedded object then (parent node, indexInParent), if after then
|
||||
* (parent node, indexInParent + 1).
|
||||
*/
|
||||
DOMPoint OffsetToDOMPoint(int32_t aOffset);
|
||||
|
||||
/**
|
||||
* Return true if the used ARIA role (if any) allows the hypertext accessible
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsIArray.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
@ -58,7 +59,7 @@ nsWinUtils::MaybeStartWindowEmulation()
|
||||
// with tabs.
|
||||
if (Compatibility::IsJAWS() || Compatibility::IsWE() ||
|
||||
Compatibility::IsDolphin() ||
|
||||
Preferences::GetBool("browser.tabs.remote")) {
|
||||
BrowserTabsRemote()) {
|
||||
RegisterNativeWindow(kClassNameTabContent);
|
||||
sHWNDCache = new nsRefPtrHashtable<nsPtrHashKey<void>, DocAccessible>(4);
|
||||
return true;
|
||||
|
@ -7,6 +7,9 @@
|
||||
pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.html");
|
||||
pref("browser.chromeURL", "chrome://browser/content/");
|
||||
|
||||
// Bug 945235: Prevent all bars to be considered visible:
|
||||
pref("toolkit.defaultChromeFeatures", "chrome,dialog=no,close,resizable,scrollbars,extrachrome");
|
||||
|
||||
// Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
|
||||
pref("browser.viewport.scaleRatio", -1);
|
||||
|
||||
@ -853,3 +856,9 @@ pref("osfile.reset_worker_delay", 5000);
|
||||
|
||||
// The URL of the Firefox Accounts auth server backend
|
||||
pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1");
|
||||
|
||||
// APZC preferences.
|
||||
//
|
||||
// Gaia relies heavily on scroll events for now, so lets fire them
|
||||
// more often than the default value (100).
|
||||
pref("apz.asyncscroll.throttle", 40);
|
||||
|
131
b2g/chrome/content/netError.css
Normal file
@ -0,0 +1,131 @@
|
||||
/* 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/. */
|
||||
|
||||
/*
|
||||
* This defines the look-and-feel styling of the error pages.
|
||||
* (see: netError.xhtml)
|
||||
*
|
||||
* Original styling by William Price <bugzilla@mob.rice.edu>
|
||||
* Updated for mobile by: Wes Johnston <wjohnston@mozilla.com>
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0 8px 8px;
|
||||
font-family: "Nokia Sans", Tahoma, sans-serif !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0px;
|
||||
padding: 0px 0px 0px 1em;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0px;
|
||||
padding: 8px 0px;
|
||||
}
|
||||
|
||||
#errorPage {
|
||||
background-color: #CEE6F4;
|
||||
}
|
||||
|
||||
#errorPage.certerror {
|
||||
background-color: #EFD400;
|
||||
}
|
||||
|
||||
#errorPage.blockedsite {
|
||||
background-color: #BF0000;
|
||||
}
|
||||
|
||||
#errorTitle {
|
||||
background: url("chrome://browser/content/images/errorpage-warning.png") left center no-repeat;
|
||||
/* Scaled by .666 of their actual size */
|
||||
background-size: 40px 40px;
|
||||
background-origin: content-box;
|
||||
min-height: 60px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#errorPage.certerror #errorTitle {
|
||||
background-image: url("chrome://browser/content/images/errorpage-larry-black.png");
|
||||
}
|
||||
|
||||
#errorPage.blockedsite #errorTitle {
|
||||
background-image: url("chrome://browser/content/images/errorpage-larry-white.png");
|
||||
color: white;
|
||||
}
|
||||
|
||||
.errorTitleText {
|
||||
padding: 0px 0px 0px 50px;
|
||||
display: inline-block;
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
#errorPageContainer {
|
||||
background-color: white;
|
||||
border: 1px solid #999999;
|
||||
border-radius: 6px;
|
||||
padding: 6px 20px 20px;
|
||||
font-size: 14px;
|
||||
max-width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#errorShortDesc > p:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#errorShortDesc > p {
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #999999;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
#errorPage.blockedsite #errorShortDesc > p {
|
||||
font-weight: bold;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
#securityOverrideDiv {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
div[collapsed] {
|
||||
padding-left: 15px;
|
||||
background-image: url("chrome://browser/skin/images/arrowright-16.png");
|
||||
background-size: 11px 11px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left 0.3em;
|
||||
}
|
||||
|
||||
div[collapsed="true"] {
|
||||
background-image: url("chrome://browser/skin/images/arrowright-16.png");
|
||||
}
|
||||
|
||||
div[collapsed="false"] {
|
||||
background-image: url("chrome://browser/skin/images/arrowdown-16.png");
|
||||
}
|
||||
|
||||
div[collapsed="true"] > p,
|
||||
div[collapsed="true"] > div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.3em !important;
|
||||
}
|
@ -223,6 +223,7 @@ let AdbController = {
|
||||
lockEnabled: undefined,
|
||||
disableAdbTimer: null,
|
||||
disableAdbTimeoutHours: 12,
|
||||
umsActive: false,
|
||||
|
||||
debug: function(str) {
|
||||
dump("AdbController: " + str + "\n");
|
||||
@ -304,6 +305,51 @@ let AdbController = {
|
||||
},
|
||||
|
||||
updateState: function() {
|
||||
this.umsActive = false;
|
||||
this.storages = navigator.getDeviceStorages('sdcard');
|
||||
this.updateStorageState(0);
|
||||
},
|
||||
|
||||
updateStorageState: function(storageIndex) {
|
||||
if (storageIndex >= this.storages.length) {
|
||||
// We've iterated through all of the storage objects, now we can
|
||||
// really do updateStateInternal.
|
||||
this.updateStateInternal();
|
||||
return;
|
||||
}
|
||||
let storage = this.storages[storageIndex];
|
||||
if (this.DEBUG) {
|
||||
this.debug("Checking availability of storage: '" +
|
||||
storage.storageName);
|
||||
}
|
||||
|
||||
let req = storage.available();
|
||||
req.onsuccess = function(e) {
|
||||
if (this.DEBUG) {
|
||||
this.debug("Storage: '" + storage.storageName + "' is '" +
|
||||
e.target.result);
|
||||
}
|
||||
if (e.target.result == 'shared') {
|
||||
// We've found a storage area that's being shared with the PC.
|
||||
// We can stop looking now.
|
||||
this.umsActive = true;
|
||||
this.updateStateInternal();
|
||||
return;
|
||||
}
|
||||
this.updateStorageState(storageIndex + 1);
|
||||
}.bind(this);
|
||||
req.onerror = function(e) {
|
||||
dump("AdbController: error querying storage availability for '" +
|
||||
this.storages[storageIndex].storageName + "' (ignoring)\n");
|
||||
this.updateStorageState(storageIndex + 1);
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
updateStateInternal: function() {
|
||||
if (this.DEBUG) {
|
||||
this.debug("updateStateInternal: called");
|
||||
}
|
||||
|
||||
if (this.remoteDebuggerEnabled === undefined ||
|
||||
this.lockEnabled === undefined ||
|
||||
this.locked === undefined) {
|
||||
@ -338,8 +384,15 @@ let AdbController = {
|
||||
this.debug("isDebugging=" + isDebugging);
|
||||
}
|
||||
|
||||
// If USB Mass Storage, USB tethering, or a debug session is active,
|
||||
// then we don't want to disable adb in an automatic fashion (i.e.
|
||||
// when the screen locks or due to timeout).
|
||||
let sysUsbConfig = libcutils.property_get("sys.usb.config");
|
||||
let rndisActive = (sysUsbConfig.split(",").indexOf("rndis") >= 0);
|
||||
let usbFuncActive = rndisActive || this.umsActive || isDebugging;
|
||||
|
||||
let enableAdb = this.remoteDebuggerEnabled &&
|
||||
(!(this.lockEnabled && this.locked) || isDebugging);
|
||||
(!(this.lockEnabled && this.locked) || usbFuncActive);
|
||||
|
||||
let useDisableAdbTimer = true;
|
||||
try {
|
||||
@ -359,7 +412,8 @@ let AdbController = {
|
||||
this.debug("updateState: enableAdb = " + enableAdb +
|
||||
" remoteDebuggerEnabled = " + this.remoteDebuggerEnabled +
|
||||
" lockEnabled = " + this.lockEnabled +
|
||||
" locked = " + this.locked);
|
||||
" locked = " + this.locked +
|
||||
" usbFuncActive = " + usbFuncActive);
|
||||
}
|
||||
|
||||
// Configure adb.
|
||||
@ -391,7 +445,7 @@ let AdbController = {
|
||||
}
|
||||
}
|
||||
if (useDisableAdbTimer) {
|
||||
if (enableAdb && !isDebugging) {
|
||||
if (enableAdb && !usbFuncActive) {
|
||||
this.startDisableAdbTimer();
|
||||
} else {
|
||||
this.stopDisableAdbTimer();
|
||||
|
@ -21,9 +21,10 @@ Cu.import('resource://gre/modules/ErrorPage.jsm');
|
||||
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
|
||||
#endif
|
||||
|
||||
// identity
|
||||
// Identity
|
||||
Cu.import('resource://gre/modules/SignInToWebsite.jsm');
|
||||
SignInToWebsiteController.init();
|
||||
Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
|
||||
|
||||
Cu.import('resource://gre/modules/DownloadsAPI.jsm');
|
||||
|
||||
@ -614,6 +615,8 @@ var shell = {
|
||||
|
||||
this.sendEvent(window, 'ContentStart');
|
||||
|
||||
Services.obs.notifyObservers(null, 'content-start', null);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
Cu.import('resource://gre/modules/OperatorApps.jsm');
|
||||
#endif
|
||||
|
@ -25,9 +25,11 @@ chrome.jar:
|
||||
|
||||
% override chrome://global/skin/media/videocontrols.css chrome://browser/content/touchcontrols.css
|
||||
% override chrome://global/content/aboutCertError.xhtml chrome://browser/content/aboutCertError.xhtml
|
||||
% override chrome://global/skin/netError.css chrome://browser/content/netError.css
|
||||
|
||||
content/ErrorPage.js (content/ErrorPage.js)
|
||||
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
|
||||
content/netError.css (content/netError.css)
|
||||
content/images/errorpage-larry-black.png (content/images/errorpage-larry-black.png)
|
||||
content/images/errorpage-larry-white.png (content/images/errorpage-larry-white.png)
|
||||
content/images/errorpage-warning.png (content/images/errorpage-warning.png)
|
||||
|
@ -72,6 +72,10 @@ component {637b0f77-2429-49a0-915f-abf5d0db8b9a} WebappsUpdateTimer.js
|
||||
contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a}
|
||||
category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400
|
||||
|
||||
# FxAccountsUIGlue.js
|
||||
component {51875c14-91d7-4b8c-b65d-3549e101228c} FxAccountsUIGlue.js
|
||||
contract @mozilla.org/fxaccounts/fxaccounts-ui-glue;1 {51875c14-91d7-4b8c-b65d-3549e101228c}
|
||||
|
||||
# HelperAppDialog.js
|
||||
component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
|
||||
contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}
|
||||
|
@ -32,6 +32,7 @@ const AUDIO_FILTERS = ['audio/basic', 'audio/L24', 'audio/mp4',
|
||||
'audio/webm'];
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, 'cpmm',
|
||||
'@mozilla.org/childprocessmessagemanager;1',
|
||||
@ -179,18 +180,39 @@ FilePicker.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
var name = 'blob';
|
||||
if (data.result.blob.type) {
|
||||
let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||
let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
|
||||
if (mimeInfo) {
|
||||
name += '.' + mimeInfo.primaryExtension;
|
||||
// The name to be shown can be part of the message, or can be taken from
|
||||
// the DOMFile (if the blob is a DOMFile).
|
||||
let name = data.result.name;
|
||||
if (!name &&
|
||||
(data.result.blob instanceof this.mParent.File) &&
|
||||
data.result.blob.name) {
|
||||
name = data.result.blob.name;
|
||||
}
|
||||
|
||||
// Let's try to remove the full path and take just the filename.
|
||||
if (name) {
|
||||
let file = new FileUtils.File(data.result.blob.name);
|
||||
if (file && file.leafName) {
|
||||
name = file.leafName;
|
||||
}
|
||||
}
|
||||
|
||||
// the fallback is a filename composed by 'blob' + extension.
|
||||
if (!name) {
|
||||
name = 'blob';
|
||||
if (data.result.blob.type) {
|
||||
let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||
let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
|
||||
if (mimeInfo) {
|
||||
name += '.' + mimeInfo.primaryExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let file = new this.mParent.File(data.result.blob,
|
||||
{ name: name,
|
||||
type: data.result.blob.type });
|
||||
|
||||
if (file) {
|
||||
this.fireSuccess(file);
|
||||
} else {
|
||||
|
129
b2g/components/FxAccountsMgmtService.jsm
Normal file
@ -0,0 +1,129 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Some specific (certified) apps need to get access to certain Firefox Accounts
|
||||
* functionality that allows them to manage accounts (this is mostly sign up,
|
||||
* sign in, logout and delete) and get information about the currently existing
|
||||
* ones.
|
||||
*
|
||||
* This service listens for requests coming from these apps, triggers the
|
||||
* appropriate Fx Accounts flows and send reponses back to the UI.
|
||||
*
|
||||
* The communication mechanism is based in mozFxAccountsContentEvent (for
|
||||
* messages coming from the UI) and mozFxAccountsChromeEvent (for messages
|
||||
* sent from the chrome side) custom events.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["FxAccountsMgmtService"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsManager",
|
||||
"resource://gre/modules/FxAccountsManager.jsm");
|
||||
|
||||
this.FxAccountsMgmtService = {
|
||||
|
||||
_sendChromeEvent: function(aMsg) {
|
||||
if (!this._shell) {
|
||||
return;
|
||||
}
|
||||
this._shell.sendCustomEvent("mozFxAccountsChromeEvent", aMsg);
|
||||
},
|
||||
|
||||
_onFullfill: function(aMsgId, aData) {
|
||||
this._sendChromeEvent({
|
||||
id: aMsgId,
|
||||
data: aData ? aData : null
|
||||
});
|
||||
},
|
||||
|
||||
_onReject: function(aMsgId, aReason) {
|
||||
this._sendChromeEvent({
|
||||
id: aMsgId,
|
||||
error: aReason ? aReason : null
|
||||
});
|
||||
},
|
||||
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "content-start", false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
this._shell = Services.wm.getMostRecentWindow("navigator:browser").shell;
|
||||
let content = this._shell.contentBrowser.contentWindow;
|
||||
content.addEventListener("mozFxAccountsContentEvent",
|
||||
FxAccountsMgmtService);
|
||||
Services.obs.removeObserver(this, "content-start");
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
let msg = aEvent.detail;
|
||||
log.debug("Got content msg " + JSON.stringify(msg));
|
||||
let self = FxAccountsMgmtService;
|
||||
|
||||
if (!msg.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = msg.data;
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(data.method) {
|
||||
case "getAccounts":
|
||||
FxAccountsManager.getAccount().then(
|
||||
account => {
|
||||
// We only expose the email and verification status so far.
|
||||
self._onFullfill(msg.id, account);
|
||||
},
|
||||
reason => {
|
||||
self._onReject(msg.id, reason);
|
||||
}
|
||||
).then(null, Components.utils.reportError);
|
||||
break;
|
||||
case "logout":
|
||||
FxAccountsManager.signOut().then(
|
||||
() => {
|
||||
self._onFullfill(msg.id);
|
||||
},
|
||||
reason => {
|
||||
self._onReject(msg.id, reason);
|
||||
}
|
||||
).then(null, Components.utils.reportError);
|
||||
break;
|
||||
case "queryAccount":
|
||||
FxAccountsManager.queryAccount(data.accountId).then(
|
||||
result => {
|
||||
self._onFullfill(msg.id, result);
|
||||
},
|
||||
reason => {
|
||||
self._onReject(msg.id, reason);
|
||||
}
|
||||
).then(null, Components.utils.reportError);
|
||||
break;
|
||||
case "signIn":
|
||||
case "signUp":
|
||||
FxAccountsManager[data.method](data.accountId, data.password).then(
|
||||
user => {
|
||||
self._onFullfill(msg.id, user);
|
||||
},
|
||||
reason => {
|
||||
self._onReject(msg.id, reason);
|
||||
}
|
||||
).then(null, Components.utils.reportError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FxAccountsMgmtService.init();
|
73
b2g/components/FxAccountsUIGlue.js
Normal file
@ -0,0 +1,73 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict"
|
||||
|
||||
const { interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
function FxAccountsUIGlue() {
|
||||
}
|
||||
|
||||
FxAccountsUIGlue.prototype = {
|
||||
|
||||
_browser: Services.wm.getMostRecentWindow("navigator:browser"),
|
||||
|
||||
signInFlow: function() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let content = this._browser.getContentWindow();
|
||||
if (!content) {
|
||||
deferred.reject("InternalErrorNoContent");
|
||||
return;
|
||||
}
|
||||
|
||||
let id = uuidgen.generateUUID().toString();
|
||||
|
||||
content.addEventListener("mozFxAccountsRPContentEvent",
|
||||
function onContentEvent(result) {
|
||||
let msg = result.detail;
|
||||
if (!msg || !msg.id || msg.id != id) {
|
||||
deferred.reject("InternalErrorWrongContentEvent");
|
||||
content.removeEventListener("mozFxAccountsRPContentEvent",
|
||||
onContentEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Got content event " + JSON.stringify(msg));
|
||||
|
||||
if (msg.error) {
|
||||
deferred.reject(msg);
|
||||
} else {
|
||||
deferred.resolve(msg.result);
|
||||
}
|
||||
content.removeEventListener("mozFxAccountsRPContentEvent",
|
||||
onContentEvent);
|
||||
});
|
||||
|
||||
let detail = {
|
||||
method: "openFlow",
|
||||
id: id
|
||||
};
|
||||
log.debug("Send chrome event " + JSON.stringify(detail));
|
||||
this._browser.shell.sendCustomEvent("mozFxAccountsRPChromeEvent", detail);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
classID: Components.ID("{51875c14-91d7-4b8c-b65d-3549e101228c}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFxAccountsUIGlue])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FxAccountsUIGlue]);
|
@ -13,6 +13,7 @@ EXTRA_COMPONENTS += [
|
||||
'ContentHandler.js',
|
||||
'ContentPermissionPrompt.js',
|
||||
'FilePicker.js',
|
||||
'FxAccountsUIGlue.js',
|
||||
'HelperAppDialog.js',
|
||||
'MailtoProtocolHandler.js',
|
||||
'PaymentGlue.js',
|
||||
@ -36,6 +37,7 @@ if CONFIG['MOZ_UPDATER']:
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ErrorPage.jsm',
|
||||
'FxAccountsMgmtService.jsm',
|
||||
'SignInToWebsite.jsm',
|
||||
'TelURIParser.jsm',
|
||||
'WebappsUpdater.jsm',
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "5bfef5faac50d14e055f642a44ed2df8483fb2fe",
|
||||
"revision": "f112883f55f9f23abd5aac107ca09f5471c37798",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -789,6 +789,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
@BINPATH@/components/TelProtocolHandler.js
|
||||
@BINPATH@/components/B2GAboutRedirector.js
|
||||
@BINPATH@/components/FilePicker.js
|
||||
@BINPATH@/components/FxAccountsUIGlue.js
|
||||
@BINPATH@/components/HelperAppDialog.js
|
||||
@BINPATH@/components/DownloadsUI.js
|
||||
|
||||
@ -796,6 +797,8 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
@BINPATH@/components/DataStoreService.js
|
||||
@BINPATH@/components/dom_datastore.xpt
|
||||
|
||||
@BINPATH@/components/services_fxaccounts.xpt
|
||||
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
@BINPATH@/components/dom_webspeechsynth.xpt
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1386360478000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1386891443000">
|
||||
<emItems>
|
||||
<emItem blockID="i454" id="sqlmoz@facebook.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
@ -106,6 +106,10 @@
|
||||
<versionRange minVersion="3.4.1" maxVersion="3.4.1.194" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i506" id="ext@bettersurfplus.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i100" id="{394DCBA4-1F92-4f8e-8EC9-8D2CB90CB69B}">
|
||||
<versionRange minVersion="2.5.0" maxVersion="2.5.0" severity="1">
|
||||
</versionRange>
|
||||
@ -453,6 +457,10 @@
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i507" id="4zffxtbr-bs@VideoDownloadConverter_4z.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
|
||||
</emItem>
|
||||
<emItem blockID="i485" id="/^brasilescape.*\@facebook\.com$//">
|
||||
@ -693,6 +701,10 @@
|
||||
<versionRange minVersion="0" maxVersion="15.0.5" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i505" id="extacylife@a.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i15" id="personas@christopher.beard">
|
||||
<versionRange minVersion="1.6" maxVersion="1.6">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
|
@ -102,9 +102,9 @@ var StarUI = {
|
||||
this.cancelButtonOnCommand();
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
if (aEvent.target.className == "expander-up" ||
|
||||
aEvent.target.className == "expander-down" ||
|
||||
aEvent.target.id == "editBMPanel_newFolderButton") {
|
||||
if (aEvent.target.classList.contains("expander-up") ||
|
||||
aEvent.target.classList.contains("expander-down") ||
|
||||
aEvent.target.id == "editBMPanel_newFolderButton") {
|
||||
//XXX Why is this necessary? The defaultPrevented check should
|
||||
// be enough.
|
||||
break;
|
||||
|
@ -742,7 +742,7 @@ var gBrowserInit = {
|
||||
delayedStartupFinished: false,
|
||||
|
||||
onLoad: function() {
|
||||
gMultiProcessBrowser = gPrefService.getBoolPref("browser.tabs.remote");
|
||||
gMultiProcessBrowser = Services.appinfo.browserTabsRemote;
|
||||
|
||||
var mustLoadSidebar = false;
|
||||
|
||||
@ -2936,27 +2936,29 @@ const BrowserSearch = {
|
||||
|
||||
let searchBar = this.searchBar;
|
||||
let placement = CustomizableUI.getPlacementOfWidget("search-container");
|
||||
let focusSearchBar = () => {
|
||||
searchBar = this.searchBar;
|
||||
searchBar.select();
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
};
|
||||
if (placement && placement.area == CustomizableUI.AREA_PANEL) {
|
||||
PanelUI.show().then(() => {
|
||||
// The panel is not constructed until the first time it is shown.
|
||||
searchBar = this.searchBar;
|
||||
searchBar.select();
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
});
|
||||
return;
|
||||
} else if (placement.area == CustomizableUI.AREA_NAVBAR && searchBar &&
|
||||
searchBar.parentNode.classList.contains("overflowedItem")) {
|
||||
let navBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||
navBar.overflowable.show().then(() => {
|
||||
// The searchBar gets moved when the overflow panel opens.
|
||||
searchBar = this.searchBar;
|
||||
searchBar.select();
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
focusSearchBar();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (searchBar && window.fullScreen) {
|
||||
FullScreen.mouseoverToggle(true);
|
||||
if (placement.area == CustomizableUI.AREA_NAVBAR && searchBar &&
|
||||
searchBar.parentNode.classList.contains("overflowedItem")) {
|
||||
let navBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||
navBar.overflowable.show().then(() => {
|
||||
focusSearchBar();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (searchBar) {
|
||||
if (window.fullScreen)
|
||||
FullScreen.mouseoverToggle(true);
|
||||
searchBar.select();
|
||||
}
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
@ -6099,33 +6101,16 @@ function undoCloseTab(aIndex) {
|
||||
if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab))
|
||||
blankTabToRemove = gBrowser.selectedTab;
|
||||
|
||||
let numberOfTabsToUndoClose = 0;
|
||||
let index = Number(aIndex);
|
||||
|
||||
|
||||
if (isNaN(index)) {
|
||||
index = 0;
|
||||
numberOfTabsToUndoClose = SessionStore.getNumberOfTabsClosedLast(window);
|
||||
} else {
|
||||
if (0 > index || index >= SessionStore.getClosedTabCount(window))
|
||||
return null;
|
||||
numberOfTabsToUndoClose = 1;
|
||||
}
|
||||
|
||||
let tab = null;
|
||||
while (numberOfTabsToUndoClose > 0 &&
|
||||
numberOfTabsToUndoClose--) {
|
||||
var tab = null;
|
||||
if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) {
|
||||
TabView.prepareUndoCloseTab(blankTabToRemove);
|
||||
tab = SessionStore.undoCloseTab(window, index);
|
||||
tab = SessionStore.undoCloseTab(window, aIndex || 0);
|
||||
TabView.afterUndoCloseTab();
|
||||
if (blankTabToRemove) {
|
||||
|
||||
if (blankTabToRemove)
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
blankTabToRemove = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the number of tabs closed last time to the default.
|
||||
SessionStore.setNumberOfTabsClosedLast(window, 1);
|
||||
return tab;
|
||||
}
|
||||
|
||||
@ -6948,13 +6933,8 @@ var TabContextMenu = {
|
||||
menuItem.disabled = disabled;
|
||||
|
||||
// Session store
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
let closedTabCount = SessionStore.getNumberOfTabsClosedLast(window);
|
||||
undoCloseTabElement.disabled = closedTabCount == 0;
|
||||
// Change the label of "Undo Close Tab" to specify if it will undo a batch-close
|
||||
// or a single close.
|
||||
let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel";
|
||||
undoCloseTabElement.setAttribute("label", undoCloseTabElement.getAttribute(visibleLabel));
|
||||
document.getElementById("context_undoCloseTab").disabled =
|
||||
SessionStore.getClosedTabCount(window) == 0;
|
||||
|
||||
// Only one of pin/unpin should be visible
|
||||
document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
|
||||
|
@ -115,8 +115,7 @@
|
||||
oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_undoCloseTab"
|
||||
singletablabel="&undoCloseTab.label;"
|
||||
multipletablabel="&undoCloseTabs.label;"
|
||||
label="&undoCloseTab.label;"
|
||||
accesskey="&undoCloseTab.accesskey;"
|
||||
observes="History:UndoCloseTab"/>
|
||||
<menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
|
||||
@ -197,6 +196,7 @@
|
||||
<!-- UI tour experience -->
|
||||
<panel id="UITourTooltip"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
noautohide="true"
|
||||
align="start"
|
||||
@ -206,6 +206,7 @@
|
||||
<description id="UITourTooltipDescription" flex="1"/>
|
||||
</panel>
|
||||
<panel id="UITourHighlightContainer"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
noautohide="true"
|
||||
consumeoutsideclicks="false">
|
||||
@ -461,7 +462,7 @@
|
||||
#ifdef MENUBAR_CAN_AUTOHIDE
|
||||
toolbarname="&menubarCmd.label;"
|
||||
accesskey="&menubarCmd.accesskey;"
|
||||
#ifdef XP_LINUX
|
||||
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
|
||||
autohide="true"
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1679,26 +1679,13 @@
|
||||
throw new Error("Invalid argument: " + aCloseTabs);
|
||||
}
|
||||
|
||||
let maxUndo =
|
||||
Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
|
||||
let warnOnCloseOtherTabs =
|
||||
Services.prefs.getBoolPref("browser.tabs.warnOnCloseOtherTabs");
|
||||
let warnOnCloseWindow =
|
||||
Services.prefs.getBoolPref("browser.tabs.warnOnClose");
|
||||
let isWindowClosing = aCloseTabs == this.closingTabsEnum.ALL;
|
||||
if (tabsToClose <= 1)
|
||||
return true;
|
||||
|
||||
let skipWarning =
|
||||
// 1) If there is only one tab to close, we'll never warn the user.
|
||||
tabsToClose <= 1 ||
|
||||
// 2) If the whole window is going to be closed, don't warn the
|
||||
// user if the user has browser.tabs.warnOnClose set to false.
|
||||
(isWindowClosing && !warnOnCloseWindow) ||
|
||||
// 3) If the number of tabs are less than the undo threshold
|
||||
// or if the user has specifically opted-in to ignoring
|
||||
// this warning via the warnOnCloseOtherTabs pref.
|
||||
(!isWindowClosing && (!warnOnCloseOtherTabs ||
|
||||
tabsToClose <= maxUndo));
|
||||
if (skipWarning)
|
||||
const pref = aCloseTabs == this.closingTabsEnum.ALL ?
|
||||
"browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
|
||||
var shouldPrompt = Services.prefs.getBoolPref(pref);
|
||||
if (!shouldPrompt)
|
||||
return true;
|
||||
|
||||
var ps = Services.prompt;
|
||||
@ -1722,16 +1709,14 @@
|
||||
+ (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
|
||||
bundle.getString("tabs.closeButtonMultiple"),
|
||||
null, null,
|
||||
bundle.getString("tabs.closeWarningPromptMe"),
|
||||
aCloseTabs == this.closingTabsEnum.ALL ?
|
||||
bundle.getString("tabs.closeWarningPromptMe") : null,
|
||||
warnOnClose);
|
||||
var reallyClose = (buttonPressed == 0);
|
||||
|
||||
// don't set the pref unless they press OK and it's false
|
||||
if (reallyClose && !warnOnClose.value) {
|
||||
let pref = isWindowClosing ? "browser.tabs.warnOnClose" :
|
||||
"browser.tabs.warnOnCloseOtherTabs";
|
||||
if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
|
||||
Services.prefs.setBoolPref(pref, false);
|
||||
}
|
||||
|
||||
return reallyClose;
|
||||
]]>
|
||||
@ -1758,11 +1743,9 @@
|
||||
<![CDATA[
|
||||
if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) {
|
||||
let tabs = this.getTabsToTheEndFrom(aTab);
|
||||
let numberOfTabsToClose = tabs.length;
|
||||
for (let i = numberOfTabsToClose - 1; i >= 0; --i) {
|
||||
for (let i = tabs.length - 1; i >= 0; --i) {
|
||||
this.removeTab(tabs[i], {animate: true});
|
||||
}
|
||||
SessionStore.setNumberOfTabsClosedLast(window, numberOfTabsToClose);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
@ -1779,14 +1762,10 @@
|
||||
let tabs = this.visibleTabs;
|
||||
this.selectedTab = aTab;
|
||||
|
||||
let closedTabs = 0;
|
||||
for (let i = tabs.length - 1; i >= 0; --i) {
|
||||
if (tabs[i] != aTab && !tabs[i].pinned) {
|
||||
if (tabs[i] != aTab && !tabs[i].pinned)
|
||||
this.removeTab(tabs[i], {animate: true});
|
||||
closedTabs++;
|
||||
}
|
||||
}
|
||||
SessionStore.setNumberOfTabsClosedLast(window, closedTabs);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
@ -1815,8 +1794,6 @@
|
||||
var byMouse = aParams.byMouse;
|
||||
}
|
||||
|
||||
SessionStore.setNumberOfTabsClosedLast(window, 1);
|
||||
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate &&
|
||||
@ -3037,7 +3014,7 @@
|
||||
"-moz-default-background-color" :
|
||||
Services.prefs.getCharPref("browser.display.background_color");
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.tabs.remote")) {
|
||||
if (Services.appinfo.browserTabsRemote) {
|
||||
messageManager.addMessageListener("DOMTitleChanged", this);
|
||||
messageManager.addMessageListener("contextmenu", this);
|
||||
}
|
||||
@ -3103,7 +3080,7 @@
|
||||
document.removeEventListener("keypress", this, false);
|
||||
window.removeEventListener("sizemodechange", this, false);
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.tabs.remote")) {
|
||||
if (Services.appinfo.browserTabsRemote) {
|
||||
messageManager.removeMessageListener("DOMTitleChanged", this);
|
||||
messageManager.removeMessageListener("contextmenu", this);
|
||||
}
|
||||
|
@ -231,8 +231,6 @@ run-if = toolkit == "cocoa"
|
||||
[browser_bug839103.js]
|
||||
[browser_bug880101.js]
|
||||
[browser_bug882977.js]
|
||||
[browser_bug887515.js]
|
||||
[browser_bug896291_closeMaxSessionStoreTabs.js]
|
||||
[browser_bug902156.js]
|
||||
[browser_bug906190.js]
|
||||
[browser_canonizeURL.js]
|
||||
|
@ -7,8 +7,7 @@ function test() {
|
||||
|
||||
ok(true, "duplicateTabIn opened a new window");
|
||||
|
||||
aSubject.addEventListener("load", function () {
|
||||
aSubject.removeEventListener("load", arguments.callee, false);
|
||||
whenDelayedStartupFinished(aSubject, function () {
|
||||
executeSoon(function () {
|
||||
aSubject.close();
|
||||
finish();
|
||||
|
@ -1,75 +0,0 @@
|
||||
function numClosedTabs()
|
||||
SessionStore.getNumberOfTabsClosedLast(window);
|
||||
|
||||
var originalTab;
|
||||
var tab1Loaded = false;
|
||||
var tab2Loaded = false;
|
||||
|
||||
function verifyUndoMultipleClose() {
|
||||
if (!tab1Loaded || !tab2Loaded)
|
||||
return;
|
||||
|
||||
gBrowser.removeAllTabsBut(originalTab);
|
||||
updateTabContextMenu();
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled.");
|
||||
is(numClosedTabs(), 2, "There should be 2 closed tabs.");
|
||||
is(gBrowser.tabs.length, 1, "There should only be 1 open tab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"),
|
||||
"The label should be showing that the command will restore multiple tabs");
|
||||
undoCloseTab();
|
||||
|
||||
is(gBrowser.tabs.length, 3, "There should be 3 open tabs");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"),
|
||||
"The label should be showing that the command will restore a single tab");
|
||||
|
||||
gBrowser.removeTabsToTheEndFrom(originalTab);
|
||||
updateTabContextMenu();
|
||||
ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled.");
|
||||
is(numClosedTabs(), 2, "There should be 2 closed tabs.");
|
||||
is(gBrowser.tabs.length, 1, "There should only be 1 open tab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"),
|
||||
"The label should be showing that the command will restore multiple tabs");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref("browser.tabs.animate", false);
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.tabs.animate");
|
||||
originalTab.linkedBrowser.loadURI("about:blank");
|
||||
originalTab = null;
|
||||
});
|
||||
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"),
|
||||
"The label should be showing that the command will restore a single tab");
|
||||
|
||||
originalTab = gBrowser.selectedTab;
|
||||
gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/");
|
||||
var tab1 = gBrowser.addTab("http://mochi.test:8888/");
|
||||
var tab2 = gBrowser.addTab("http://mochi.test:8888/");
|
||||
var browser1 = gBrowser.getBrowserForTab(tab1);
|
||||
browser1.addEventListener("load", function onLoad1() {
|
||||
browser1.removeEventListener("load", onLoad1, true);
|
||||
tab1Loaded = true;
|
||||
tab1 = null;
|
||||
|
||||
verifyUndoMultipleClose();
|
||||
}, true);
|
||||
var browser2 = gBrowser.getBrowserForTab(tab2);
|
||||
browser2.addEventListener("load", function onLoad2() {
|
||||
browser2.removeEventListener("load", onLoad2, true);
|
||||
tab2Loaded = true;
|
||||
tab2 = null;
|
||||
|
||||
verifyUndoMultipleClose();
|
||||
}, true);
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
function numClosedTabs()
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore).
|
||||
getNumberOfTabsClosedLast(window);
|
||||
|
||||
let originalTab;
|
||||
let maxTabsUndo;
|
||||
let maxTabsUndoPlusOne;
|
||||
let acceptRemoveAllTabsDialogListener;
|
||||
let cancelRemoveAllTabsDialogListener;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
Services.prefs.setBoolPref("browser.tabs.animate", false);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.tabs.animate");
|
||||
|
||||
originalTab.linkedBrowser.loadURI("about:blank");
|
||||
originalTab = null;
|
||||
});
|
||||
|
||||
// Creating and throwing away this tab guarantees that the
|
||||
// number of tabs closed in the previous tab-close operation is 1.
|
||||
let throwaway_tab = gBrowser.addTab("http://mochi.test:8888/");
|
||||
gBrowser.removeTab(throwaway_tab);
|
||||
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"),
|
||||
"The label should be showing that the command will restore a single tab");
|
||||
|
||||
originalTab = gBrowser.selectedTab;
|
||||
gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/");
|
||||
|
||||
maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
|
||||
maxTabsUndoPlusOne = maxTabsUndo + 1;
|
||||
let numberOfTabsLoaded = 0;
|
||||
for (let i = 0; i < maxTabsUndoPlusOne; i++) {
|
||||
let tab = gBrowser.addTab("http://mochi.test:8888/");
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
|
||||
if (++numberOfTabsLoaded == maxTabsUndoPlusOne)
|
||||
verifyUndoMultipleClose();
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyUndoMultipleClose() {
|
||||
info("all tabs opened and loaded");
|
||||
cancelRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", cancelRemoveAllTabsDialog);
|
||||
Services.wm.addListener(cancelRemoveAllTabsDialogListener);
|
||||
gBrowser.removeAllTabsBut(originalTab);
|
||||
}
|
||||
|
||||
function cancelRemoveAllTabsDialog(domWindow) {
|
||||
ok(true, "dialog appeared in response to multiple tab close action");
|
||||
domWindow.document.documentElement.cancelDialog();
|
||||
Services.wm.removeListener(cancelRemoveAllTabsDialogListener);
|
||||
|
||||
acceptRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", acceptRemoveAllTabsDialog);
|
||||
Services.wm.addListener(acceptRemoveAllTabsDialogListener);
|
||||
waitForCondition(function () gBrowser.tabs.length == 1 + maxTabsUndoPlusOne, function verifyCancel() {
|
||||
is(gBrowser.tabs.length, 1 + maxTabsUndoPlusOne, /* The '1 +' is for the original tab */
|
||||
"All tabs should still be open after the 'Cancel' option on the prompt is chosen");
|
||||
gBrowser.removeAllTabsBut(originalTab);
|
||||
}, "Waited too long to find that no tabs were closed.");
|
||||
}
|
||||
|
||||
function acceptRemoveAllTabsDialog(domWindow) {
|
||||
ok(true, "dialog appeared in response to multiple tab close action");
|
||||
domWindow.document.documentElement.acceptDialog();
|
||||
Services.wm.removeListener(acceptRemoveAllTabsDialogListener);
|
||||
|
||||
waitForCondition(function () gBrowser.tabs.length == 1, function verifyAccept() {
|
||||
is(gBrowser.tabs.length, 1,
|
||||
"All other tabs should be closed after the 'OK' option on the prompt is chosen");
|
||||
finish();
|
||||
}, "Waited too long for the other tabs to be closed.");
|
||||
}
|
||||
|
||||
function WindowListener(aURL, aCallback) {
|
||||
this.callback = aCallback;
|
||||
this.url = aURL;
|
||||
}
|
||||
WindowListener.prototype = {
|
||||
onOpenWindow: function(aXULWindow) {
|
||||
var domWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
var self = this;
|
||||
domWindow.addEventListener("load", function() {
|
||||
domWindow.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
info("domWindow.document.location.href: " + domWindow.document.location.href);
|
||||
if (domWindow.document.location.href != self.url)
|
||||
return;
|
||||
|
||||
// Allow other window load listeners to execute before passing to callback
|
||||
executeSoon(function() {
|
||||
self.callback(domWindow);
|
||||
});
|
||||
}, false);
|
||||
},
|
||||
onCloseWindow: function(aXULWindow) {},
|
||||
onWindowTitleChange: function(aXULWindow, aNewTitle) {}
|
||||
}
|
@ -212,7 +212,8 @@ function test1C() {
|
||||
is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1C");
|
||||
|
||||
// remove tabs
|
||||
gTestWin.gBrowser.removeAllTabsBut(mainTab);
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
|
||||
gTestWin.gBrowser.selectTabAtIndex(0);
|
||||
|
||||
var childTabLink = gHttpTestRoot2 + "file_bug906190_2.html";
|
||||
@ -269,7 +270,8 @@ function test2C() {
|
||||
is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 2C");
|
||||
|
||||
// remove tabs
|
||||
gTestWin.gBrowser.removeAllTabsBut(mainTab);
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
|
||||
gTestWin.gBrowser.selectTabAtIndex(0);
|
||||
|
||||
// file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
|
||||
@ -336,7 +338,8 @@ function test3E() {
|
||||
is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 3E");
|
||||
|
||||
// remove tabs
|
||||
gTestWin.gBrowser.removeAllTabsBut(mainTab);
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
|
||||
gTestWin.gBrowser.selectTabAtIndex(0);
|
||||
|
||||
var childTabLink = gHttpTestRoot1 + "file_bug906190_3_4.html";
|
||||
@ -403,7 +406,8 @@ function test4E() {
|
||||
is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 4E");
|
||||
|
||||
// remove tabs
|
||||
gTestWin.gBrowser.removeAllTabsBut(mainTab);
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
|
||||
gTestWin.gBrowser.selectTabAtIndex(0);
|
||||
|
||||
// the sjs files returns a 302 redirect- note, same origins
|
||||
@ -462,7 +466,8 @@ function test5C() {
|
||||
todo_is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 5C!");
|
||||
|
||||
// remove tabs
|
||||
gTestWin.gBrowser.removeAllTabsBut(mainTab);
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
|
||||
gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
|
||||
gTestWin.gBrowser.selectTabAtIndex(0);
|
||||
|
||||
// the sjs files returns a 302 redirect - note, different origins
|
||||
|
@ -17,3 +17,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/aurora/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
browser/branding/aurora/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
14
browser/branding/aurora/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #331e54;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #181327;
|
||||
}
|
||||
|
@ -17,3 +17,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/nightly/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
browser/branding/nightly/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
14
browser/branding/nightly/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #002147;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #0a111c;
|
||||
}
|
||||
|
@ -16,3 +16,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/official/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
browser/branding/official/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
14
browser/branding/official/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #0095dd;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #036fa4;
|
||||
}
|
||||
|
@ -17,3 +17,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/unofficial/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
browser/branding/unofficial/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
14
browser/branding/unofficial/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #002147;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #0a111c;
|
||||
}
|
||||
|
@ -85,6 +85,18 @@ let gTests = [
|
||||
ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Ctrl+K should focus the search bar if it is in the navbar and not overflowing.",
|
||||
run: function() {
|
||||
let searchbar = document.getElementById("searchbar");
|
||||
let placement = CustomizableUI.getPlacementOfWidget("search-container");
|
||||
is(placement.area, CustomizableUI.AREA_NAVBAR, "Should be in nav-bar");
|
||||
|
||||
sendWebSearchKeyCommand();
|
||||
logActiveElement();
|
||||
is(document.activeElement, searchbar.textbox.inputField, "The searchbar should be focused");
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function test() {
|
||||
|
@ -481,7 +481,7 @@ BrowserGlue.prototype = {
|
||||
SessionStore.init();
|
||||
BrowserUITelemetry.init();
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.tabs.remote"))
|
||||
if (Services.appinfo.browserTabsRemote)
|
||||
ContentClick.init();
|
||||
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
||||
|
@ -275,7 +275,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/connection.xul",
|
||||
"mozilla:connectionmanager",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
null);
|
||||
},
|
||||
|
||||
@ -441,7 +441,7 @@ var gAdvancedPane = {
|
||||
introText : bundlePreferences.getString("offlinepermissionstext") };
|
||||
openDialog("chrome://browser/content/preferences/permissions.xul",
|
||||
"Browser:Permissions",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
params);
|
||||
},
|
||||
|
||||
@ -814,7 +814,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://pippki/content/certManager.xul",
|
||||
"mozilla:certmanager",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -824,7 +824,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://mozapps/content/preferences/ocsp.xul",
|
||||
"mozilla:crlmanager",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -834,7 +834,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://pippki/content/device_manager.xul",
|
||||
"mozilla:devicemanager",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
}
|
||||
#ifdef HAVE_SHELL_SERVICE
|
||||
,
|
||||
|
@ -471,7 +471,7 @@ var gPrivacyPane = {
|
||||
introText : bundlePreferences.getString("cookiepermissionstext") };
|
||||
openDialog("chrome://browser/content/preferences/permissions.xul",
|
||||
"Browser:Permissions",
|
||||
"model=yes", params);
|
||||
"modal=yes", params);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -481,7 +481,7 @@ var gPrivacyPane = {
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/cookies.xul",
|
||||
"Browser:Cookies",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
// CLEAR PRIVATE DATA
|
||||
@ -500,7 +500,7 @@ var gPrivacyPane = {
|
||||
showClearPrivateDataSettings: function ()
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/sanitize.xul",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
|
||||
|
@ -57,7 +57,7 @@ var gSecurityPane = {
|
||||
|
||||
openDialog("chrome://browser/content/preferences/permissions.xul",
|
||||
"Browser:Permissions",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
params);
|
||||
},
|
||||
|
||||
@ -111,7 +111,7 @@ var gSecurityPane = {
|
||||
{
|
||||
openDialog("chrome://passwordmgr/content/passwordManagerExceptions.xul",
|
||||
"Toolkit:PasswordManagerExceptions",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
null);
|
||||
},
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
support-files = head.js
|
||||
|
||||
[browser_bug400731.js]
|
||||
# [browser_bug415846.js]
|
||||
# Disabled for too many intermittent failures (bug 546169)
|
||||
# Previously disabled on Mac because of its bizarre special-and-unique
|
||||
[browser_bug415846.js]
|
||||
skip-if = os == "mac"
|
||||
# Disabled on Mac because of its bizarre special-and-unique
|
||||
# snowflake of a help menu.
|
||||
|
@ -37,11 +37,13 @@ function testNormal_PopupListener() {
|
||||
|
||||
// Now launch the phishing test. Can't use onload here because error pages don't
|
||||
// fire normal load events.
|
||||
window.addEventListener("DOMContentLoaded", testPhishing, true);
|
||||
content.location = "http://www.mozilla.org/firefox/its-a-trap.html";
|
||||
setTimeout(testPhishing, 2000);
|
||||
}
|
||||
|
||||
function testPhishing() {
|
||||
window.removeEventListener("DOMContentLoaded", testPhishing, true);
|
||||
|
||||
menu.addEventListener("popupshown", testPhishing_PopupListener, false);
|
||||
menu.openPopup(null, "", 0, 0, false, null);
|
||||
}
|
||||
|
@ -392,7 +392,12 @@ let MessageQueue = {
|
||||
|
||||
let sync = options && options.sync;
|
||||
let startID = (options && options.id) || this._id;
|
||||
let sendMessage = sync ? sendSyncMessage : sendAsyncMessage;
|
||||
|
||||
// We use sendRpcMessage in the sync case because we may have been called
|
||||
// through a CPOW. RPC messages are the only synchronous messages that the
|
||||
// child is allowed to send to the parent while it is handling a CPOW
|
||||
// request.
|
||||
let sendMessage = sync ? sendRpcMessage : sendAsyncMessage;
|
||||
|
||||
let data = {};
|
||||
for (let [key, id] of this._lastUpdated) {
|
||||
|
@ -25,7 +25,7 @@ interface nsIDOMNode;
|
||||
* |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(63a4d9f4-373f-11e3-a237-fa91a24410d2)]
|
||||
[scriptable, uuid(0c99811f-6c5f-4a78-9c31-2d266d714175)]
|
||||
interface nsISessionStore : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -100,20 +100,6 @@ interface nsISessionStore : nsISupports
|
||||
nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab,
|
||||
[optional] in long aDelta);
|
||||
|
||||
/**
|
||||
* Set the number of tabs that was closed during the last close-tabs
|
||||
* operation. This helps us keep track of batch-close operations so
|
||||
* we can restore multiple tabs at once.
|
||||
*/
|
||||
void setNumberOfTabsClosedLast(in nsIDOMWindow aWindow, in unsigned long aNumber);
|
||||
|
||||
/**
|
||||
* Get the number of tabs that was closed during the last close-tabs
|
||||
* operation. This helps us keep track of batch-close operations so
|
||||
* we can restore multiple tabs at once.
|
||||
*/
|
||||
unsigned long getNumberOfTabsClosedLast(in nsIDOMWindow aWindow);
|
||||
|
||||
/**
|
||||
* Get the number of restore-able tabs for a browser window
|
||||
*/
|
||||
|
@ -66,7 +66,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
||||
let restoreAllTabs = fragment.appendChild(doc.createElementNS(kNSXUL, aTagName));
|
||||
restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName("menuRestoreAllTabs.label"));
|
||||
restoreAllTabs.setAttribute("oncommand",
|
||||
"for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab(0);");
|
||||
"for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();");
|
||||
}
|
||||
return fragment;
|
||||
},
|
||||
|
@ -86,10 +86,6 @@ const BROWSER_EVENTS = [
|
||||
// The number of milliseconds in a day
|
||||
const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0;
|
||||
|
||||
#ifndef XP_WIN
|
||||
#define BROKEN_WM_Z_ORDER
|
||||
#endif
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
|
||||
@ -104,14 +100,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
|
||||
"@mozilla.org/gfx/screenmanager;1", "nsIScreenManager");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
|
||||
"resource:///modules/devtools/scratchpad-manager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
|
||||
"resource:///modules/sessionstore/DocShellCapabilities.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
|
||||
"resource:///modules/sessionstore/Messenger.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageStyle",
|
||||
"resource:///modules/sessionstore/PageStyle.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
|
||||
"resource:///modules/devtools/scratchpad-manager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
|
||||
"resource:///modules/sessionstore/SessionSaver.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
|
||||
@ -195,14 +193,6 @@ this.SessionStore = {
|
||||
return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta);
|
||||
},
|
||||
|
||||
getNumberOfTabsClosedLast: function ss_getNumberOfTabsClosedLast(aWindow) {
|
||||
return SessionStoreInternal.getNumberOfTabsClosedLast(aWindow);
|
||||
},
|
||||
|
||||
setNumberOfTabsClosedLast: function ss_setNumberOfTabsClosedLast(aWindow, aNumber) {
|
||||
return SessionStoreInternal.setNumberOfTabsClosedLast(aWindow, aNumber);
|
||||
},
|
||||
|
||||
getClosedTabCount: function ss_getClosedTabCount(aWindow) {
|
||||
return SessionStoreInternal.getClosedTabCount(aWindow);
|
||||
},
|
||||
@ -389,7 +379,7 @@ let SessionStoreInternal = {
|
||||
throw new Error("SessionStore.init() must only be called once!");
|
||||
}
|
||||
|
||||
this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote");
|
||||
this._disabledForMultiProcess = Services.appinfo.browserTabsRemote;
|
||||
if (this._disabledForMultiProcess) {
|
||||
this._deferredInitialized.resolve();
|
||||
return;
|
||||
@ -1618,35 +1608,6 @@ let SessionStoreInternal = {
|
||||
return newTab;
|
||||
},
|
||||
|
||||
setNumberOfTabsClosedLast: function ssi_setNumberOfTabsClosedLast(aWindow, aNumber) {
|
||||
if (this._disabledForMultiProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!("__SSi" in aWindow)) {
|
||||
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
return NumberOfTabsClosedLastPerWindow.set(aWindow, aNumber);
|
||||
},
|
||||
|
||||
/* Used to undo batch tab-close operations. Defaults to 1. */
|
||||
getNumberOfTabsClosedLast: function ssi_getNumberOfTabsClosedLast(aWindow) {
|
||||
if (this._disabledForMultiProcess) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!("__SSi" in aWindow)) {
|
||||
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
// Blank tabs cannot be undo-closed, so the number returned by
|
||||
// the NumberOfTabsClosedLastPerWindow can be greater than the
|
||||
// return value of getClosedTabCount. We won't restore blank
|
||||
// tabs, so we return the minimum of these two values.
|
||||
return Math.min(NumberOfTabsClosedLastPerWindow.get(aWindow) || 1,
|
||||
this.getClosedTabCount(aWindow));
|
||||
},
|
||||
|
||||
getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._windows[aWindow.__SSi]._closedTabs.length;
|
||||
@ -2478,8 +2439,24 @@ let SessionStoreInternal = {
|
||||
this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
|
||||
}
|
||||
}
|
||||
|
||||
let newClosedTabsData = winData._closedTabs || [];
|
||||
|
||||
if (overwriteTabs || firstWindow) {
|
||||
this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
|
||||
// Overwrite existing closed tabs data when overwriteTabs=true
|
||||
// or we're the first window to be restored.
|
||||
this._windows[aWindow.__SSi]._closedTabs = newClosedTabsData;
|
||||
} else if (this._max_tabs_undo > 0) {
|
||||
// If we merge tabs, we also want to merge closed tabs data. We'll assume
|
||||
// the restored tabs were closed more recently and append the current list
|
||||
// of closed tabs to the new one...
|
||||
newClosedTabsData =
|
||||
newClosedTabsData.concat(this._windows[aWindow.__SSi]._closedTabs);
|
||||
|
||||
// ... and make sure that we don't exceed the max number of closed tabs
|
||||
// we can restore.
|
||||
this._windows[aWindow.__SSi]._closedTabs =
|
||||
newClosedTabsData.slice(0, this._max_tabs_undo);
|
||||
}
|
||||
|
||||
this.restoreTabs(aWindow, tabs, winData.tabs,
|
||||
@ -3199,32 +3176,7 @@ let SessionStoreInternal = {
|
||||
* @returns Window reference
|
||||
*/
|
||||
_getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() {
|
||||
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win)
|
||||
return null;
|
||||
if (!win.closed)
|
||||
return win;
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
win = null;
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
// this is oldest to newest, so this gets a bit ugly
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
let nextWin = windowsEnum.getNext();
|
||||
if (!nextWin.closed)
|
||||
win = nextWin;
|
||||
}
|
||||
return win;
|
||||
#else
|
||||
var windowsEnum =
|
||||
Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
win = windowsEnum.getNext();
|
||||
if (!win.closed)
|
||||
return win;
|
||||
}
|
||||
return null;
|
||||
#endif
|
||||
return RecentWindow.getMostRecentBrowserWindow({ allowPopups: true });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -4040,11 +3992,6 @@ let DirtyWindows = {
|
||||
}
|
||||
};
|
||||
|
||||
// A map storing the number of tabs last closed per windoow. This only
|
||||
// stores the most recent tab-close operation, and is used to undo
|
||||
// batch tab-closing operations.
|
||||
let NumberOfTabsClosedLastPerWindow = new WeakMap();
|
||||
|
||||
// This is used to help meter the number of restoring tabs. This is the control
|
||||
// point for telling the next tab to restore. It gets attached to each gBrowser
|
||||
// via gBrowser.addTabsProgressListener
|
||||
|
@ -56,6 +56,7 @@ support-files =
|
||||
[browser_formdata_format.js]
|
||||
[browser_global_store.js]
|
||||
[browser_input.js]
|
||||
[browser_merge_closed_tabs.js]
|
||||
[browser_pageshow.js]
|
||||
[browser_pageStyle.js]
|
||||
[browser_sessionStorage.js]
|
||||
|
@ -40,8 +40,4 @@ function test() {
|
||||
"Invalid window for getWindowValue throws");
|
||||
ok(test(function() ss.setWindowValue({}, "", "")),
|
||||
"Invalid window for setWindowValue throws");
|
||||
ok(test(function() ss.getNumberOfTabsClosedLast({})),
|
||||
"Invalid window for getNumberOfTabsClosedLast throws");
|
||||
ok(test(function() ss.setNumberOfTabsClosedLast({}, 1)),
|
||||
"Invalid window for setNumberOfTabsClosedLast throws");
|
||||
}
|
||||
|
@ -1,53 +1,38 @@
|
||||
/* 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/. */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 597071 **/
|
||||
/**
|
||||
* Bug 597071 - Closed windows should only be resurrected when there is a single
|
||||
* popup window
|
||||
*/
|
||||
add_task(function test_close_last_nonpopup_window() {
|
||||
// Purge the list of closed windows.
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
let oldState = ss.getWindowState(window);
|
||||
|
||||
// set the pref to 1 greater than it currently is so we have room for an extra
|
||||
// closed window
|
||||
let closedWindowCount = ss.getClosedWindowCount();
|
||||
Services.prefs.setIntPref("browser.sessionstore.max_windows_undo",
|
||||
closedWindowCount + 1);
|
||||
let popupState = {windows: [
|
||||
{tabs: [{entries: []}], isPopup: true, hidden: "toolbar"}
|
||||
]};
|
||||
|
||||
let currentState = ss.getBrowserState();
|
||||
let popupState = { windows:[
|
||||
{ tabs:[ {entries:[] }], isPopup: true, hidden: "toolbar" }
|
||||
] };
|
||||
|
||||
// set this window to be a popup.
|
||||
// Set this window to be a popup.
|
||||
ss.setWindowState(window, JSON.stringify(popupState), true);
|
||||
|
||||
// open a new non-popup window
|
||||
let newWin = openDialog(location, "", "chrome,all,dialog=no", "http://example.com");
|
||||
newWin.addEventListener("load", function(aEvent) {
|
||||
newWin.removeEventListener("load", arguments.callee, false);
|
||||
// Open a new window with a tab.
|
||||
let win = yield promiseNewWindowLoaded({private: false});
|
||||
let tab = win.gBrowser.addTab("http://example.com/");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
newWin.gBrowser.addEventListener("load", function(aEvent) {
|
||||
newWin.gBrowser.removeEventListener("load", arguments.callee, true);
|
||||
// Make sure sessionstore sees this window.
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows.length, 2, "sessionstore knows about this window");
|
||||
|
||||
newWin.gBrowser.addTab().linkedBrowser.stop();
|
||||
|
||||
// make sure sessionstore sees this window
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows.length, 2, "sessionstore knows about this window");
|
||||
|
||||
newWin.close();
|
||||
newWin.addEventListener("unload", function(aEvent) {
|
||||
newWin.removeEventListener("unload", arguments.callee, false);
|
||||
|
||||
is(ss.getClosedWindowCount(), closedWindowCount + 1,
|
||||
"increased closed window count");
|
||||
|
||||
Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
|
||||
ss.setBrowserState(currentState);
|
||||
executeSoon(finish);
|
||||
|
||||
}, false);
|
||||
}, true);
|
||||
}, false);
|
||||
}
|
||||
// Closed the window and check the closed window count.
|
||||
yield promiseWindowClosed(win);
|
||||
is(ss.getClosedWindowCount(), 1, "correct closed window count");
|
||||
|
||||
// Cleanup.
|
||||
ss.setWindowState(window, oldState, true);
|
||||
});
|
||||
|
@ -0,0 +1,71 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This test ensures that closed tabs are merged when restoring
|
||||
* a window state without overwriting tabs.
|
||||
*/
|
||||
add_task(function () {
|
||||
const initialState = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:blank" }] }
|
||||
],
|
||||
_closedTabs: [
|
||||
{ state: { entries: [{ ID: 1000, url: "about:blank" }]} },
|
||||
{ state: { entries: [{ ID: 1001, url: "about:blank" }]} }
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
const restoreState = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:robots" }] }
|
||||
],
|
||||
_closedTabs: [
|
||||
{ state: { entries: [{ ID: 1002, url: "about:robots" }]} },
|
||||
{ state: { entries: [{ ID: 1003, url: "about:robots" }]} },
|
||||
{ state: { entries: [{ ID: 1004, url: "about:robots" }]} }
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
const maxTabsUndo = 4;
|
||||
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", maxTabsUndo);
|
||||
|
||||
// Open a new window and restore it to an initial state.
|
||||
let win = yield promiseNewWindowLoaded({private: false});
|
||||
SessionStore.setWindowState(win, JSON.stringify(initialState), true);
|
||||
is(SessionStore.getClosedTabCount(win), 2, "2 closed tabs after restoring initial state");
|
||||
|
||||
// Restore the new state but do not overwrite existing tabs (this should
|
||||
// cause the closed tabs to be merged).
|
||||
SessionStore.setWindowState(win, JSON.stringify(restoreState), false);
|
||||
|
||||
// Verify the windows closed tab data is correct.
|
||||
let iClosed = initialState.windows[0]._closedTabs;
|
||||
let rClosed = restoreState.windows[0]._closedTabs;
|
||||
let cData = JSON.parse(SessionStore.getClosedTabData(win));
|
||||
|
||||
is(cData.length, Math.min(iClosed.length + rClosed.length, maxTabsUndo),
|
||||
"Number of closed tabs is correct");
|
||||
|
||||
// When the closed tabs are merged the restored tabs are considered to be
|
||||
// closed more recently.
|
||||
for (let i = 0; i < cData.length; i++) {
|
||||
if (i < rClosed.length) {
|
||||
is(cData[i].state.entries[0].ID, rClosed[i].state.entries[0].ID,
|
||||
"Closed tab entry matches");
|
||||
} else {
|
||||
is(cData[i].state.entries[0].ID, iClosed[i - rClosed.length].state.entries[0].ID,
|
||||
"Closed tab entry matches");
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
win.close();
|
||||
});
|
||||
|
||||
|
@ -412,6 +412,29 @@ function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
whenDelayedStartupFinished(win, () => aCallback(win));
|
||||
return win;
|
||||
}
|
||||
function promiseNewWindowLoaded(aOptions) {
|
||||
let deferred = Promise.defer();
|
||||
whenNewWindowLoaded(aOptions, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chrome windows aren't closed synchronously. Provide a helper method to close
|
||||
* a window and wait until we received the "domwindowclosed" notification for it.
|
||||
*/
|
||||
function promiseWindowClosed(win) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
Services.obs.addObserver(function obs(subject, topic) {
|
||||
if (subject == win) {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, "domwindowclosed", false);
|
||||
|
||||
win.close();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* This waits for the browser-delayed-startup-finished notification of a given
|
||||
|
@ -752,10 +752,9 @@ WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
aImage->GetFrame(imgIContainer::FRAME_FIRST,
|
||||
imgIContainer::FLAG_SYNC_DECODE,
|
||||
getter_AddRefs(surface));
|
||||
nsRefPtr<gfxASurface> surface =
|
||||
aImage->GetFrame(imgIContainer::FRAME_FIRST,
|
||||
imgIContainer::FLAG_SYNC_DECODE);
|
||||
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
|
||||
|
||||
nsRefPtr<gfxImageSurface> image(surface->GetAsReadableARGB32ImageSurface());
|
||||
|
@ -38,5 +38,5 @@ function onTabViewWindowLoaded() {
|
||||
gBrowser.removeTab(tabTwo);
|
||||
finish();
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ function test() {
|
||||
|
||||
createBlankTab();
|
||||
afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab);
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ function test() {
|
||||
gBrowser.removeTab(gBrowser.tabs[0]);
|
||||
|
||||
afterAllTabsLoaded(finishTest);
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ function test() {
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
hideTabView(finishTest);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
whenTabViewIsHidden(function() {
|
||||
win.gBrowser.removeTab(win.gBrowser.selectedTab);
|
||||
executeSoon(function() {
|
||||
win.undoCloseTab(0);
|
||||
win.undoCloseTab();
|
||||
|
||||
groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) {
|
||||
groupItemTwo.removeSubscriber("childAdded", onChildAdded);
|
||||
|
@ -362,7 +362,7 @@ function newWindowWithState(state, callback) {
|
||||
function restoreTab(callback, index, win) {
|
||||
win = win || window;
|
||||
|
||||
let tab = win.undoCloseTab(index);
|
||||
let tab = win.undoCloseTab(index || 0);
|
||||
let tabItem = tab._tabViewTabItem;
|
||||
|
||||
let finalize = function () {
|
||||
|
@ -1698,7 +1698,13 @@ Breakpoints.prototype = {
|
||||
// By default, new breakpoints are always enabled. Disabled breakpoints
|
||||
// are, in fact, removed from the server but preserved in the frontend,
|
||||
// so that they may not be forgotten across target navigations.
|
||||
this._disabled.delete(identifier);
|
||||
let disabledPromise = this._disabled.get(identifier);
|
||||
if (disabledPromise) {
|
||||
disabledPromise.then(({ conditionalExpression: previousValue }) => {
|
||||
aBreakpointClient.conditionalExpression = previousValue;
|
||||
});
|
||||
this._disabled.delete(identifier);
|
||||
}
|
||||
|
||||
// Preserve information about the breakpoint's line text, to display it
|
||||
// in the sources pane without requiring fetching the source (for example,
|
||||
|
@ -1357,7 +1357,11 @@ VariableBubbleView.prototype = {
|
||||
|
||||
let scriptLine = hoveredLine - scriptLineOffset;
|
||||
let scriptColumn = hoveredColumn - scriptColumnOffset;
|
||||
let identifierInfo = parsedSource.getIdentifierAt(scriptLine + 1, scriptColumn);
|
||||
let identifierInfo = parsedSource.getIdentifierAt({
|
||||
line: scriptLine + 1,
|
||||
column: scriptColumn,
|
||||
scriptIndex: scriptInfo.index
|
||||
});
|
||||
|
||||
// If the info is null, we're not hovering any identifier.
|
||||
if (!identifierInfo) {
|
||||
|
@ -53,3 +53,7 @@
|
||||
#body[layout=horizontal] #vertical-layout-panes-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #stackframes {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ support-files =
|
||||
doc_random-javascript.html
|
||||
doc_recursion-stack.html
|
||||
doc_scope-variable.html
|
||||
doc_scope-variable-2.html
|
||||
doc_scope-variable-3.html
|
||||
doc_script-switching-01.html
|
||||
doc_script-switching-02.html
|
||||
doc_step-out.html
|
||||
@ -94,6 +96,7 @@ support-files =
|
||||
[browser_dbg_cmd-dbg.js]
|
||||
[browser_dbg_conditional-breakpoints-01.js]
|
||||
[browser_dbg_conditional-breakpoints-02.js]
|
||||
[browser_dbg_conditional-breakpoints-03.js]
|
||||
[browser_dbg_controller-evaluate-01.js]
|
||||
[browser_dbg_controller-evaluate-02.js]
|
||||
[browser_dbg_debugger-statement.js]
|
||||
@ -210,6 +213,8 @@ support-files =
|
||||
[browser_dbg_variables-view-frozen-sealed-nonext.js]
|
||||
[browser_dbg_variables-view-hide-non-enums.js]
|
||||
[browser_dbg_variables-view-large-array-buffer.js]
|
||||
[browser_dbg_variables-view-override-01.js]
|
||||
[browser_dbg_variables-view-override-02.js]
|
||||
[browser_dbg_variables-view-popup-01.js]
|
||||
[browser_dbg_variables-view-popup-02.js]
|
||||
[browser_dbg_variables-view-popup-03.js]
|
||||
@ -218,6 +223,7 @@ support-files =
|
||||
[browser_dbg_variables-view-popup-06.js]
|
||||
[browser_dbg_variables-view-popup-07.js]
|
||||
[browser_dbg_variables-view-popup-08.js]
|
||||
[browser_dbg_variables-view-popup-09.js]
|
||||
[browser_dbg_variables-view-reexpand-01.js]
|
||||
[browser_dbg_variables-view-reexpand-02.js]
|
||||
[browser_dbg_variables-view-webidl.js]
|
||||
|
@ -0,0 +1,82 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that conditional breakpoint expressions survive disabled breakpoints.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
|
||||
|
||||
function test() {
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gSources, gBreakpoints, gLocation;
|
||||
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gBreakpoints = gDebugger.DebuggerController.Breakpoints;
|
||||
|
||||
gLocation = { url: gSources.selectedValue, line: 18 };
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 17)
|
||||
.then(addBreakpoint)
|
||||
.then(setConditional)
|
||||
.then(() => {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_REMOVED);
|
||||
toggleBreakpoint();
|
||||
return finished;
|
||||
})
|
||||
.then(() => {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_ADDED);
|
||||
toggleBreakpoint();
|
||||
return finished;
|
||||
})
|
||||
.then(testConditionalExpressionOnClient)
|
||||
.then(() => {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWING);
|
||||
openConditionalPopup();
|
||||
return finished;
|
||||
})
|
||||
.then(testConditionalExpressionInPopup)
|
||||
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
gDebuggee.ermahgerd();
|
||||
});
|
||||
|
||||
function addBreakpoint() {
|
||||
return gPanel.addBreakpoint(gLocation);
|
||||
}
|
||||
|
||||
function setConditional(aClient) {
|
||||
aClient.conditionalExpression = "hello";
|
||||
}
|
||||
|
||||
function toggleBreakpoint() {
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gDebugger.document.querySelector(".dbg-breakpoint-checkbox"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function openConditionalPopup() {
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gDebugger.document.querySelector(".dbg-breakpoint"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function testConditionalExpressionOnClient() {
|
||||
return gBreakpoints._getAdded(gLocation).then(aClient => {
|
||||
is(aClient.conditionalExpression, "hello", "The expression is correct (1).");
|
||||
});
|
||||
}
|
||||
|
||||
function testConditionalExpressionInPopup() {
|
||||
let textbox = gDebugger.document.getElementById("conditional-breakpoint-panel-textbox");
|
||||
is(textbox.value, "hello", "The expression is correct (2).")
|
||||
}
|
||||
}
|
@ -33,44 +33,44 @@ function test() {
|
||||
is(parsed.scriptCount, 3,
|
||||
"There should be 3 scripts parsed in the parent HTML source.");
|
||||
|
||||
is(parsed.getScriptInfo(0).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(0).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"There is no script at the beginning of the parent source.");
|
||||
is(parsed.getScriptInfo(source.length - 1).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.length - 1).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"There is no script at the end of the parent source.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:31, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:31, length:13, index:0})",
|
||||
"The first script was located correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13, index:1})",
|
||||
"The second script was located correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:151, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:151, length:13, index:2})",
|
||||
"The third script was located correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") - 1).toSource(), "({start:31, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") - 1).toSource(), "({start:31, length:13, index:0})",
|
||||
"The left edge of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") - 1).toSource(), "({start:85, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") - 1).toSource(), "({start:85, length:13, index:1})",
|
||||
"The left edge of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") - 1).toSource(), "({start:151, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") - 1).toSource(), "({start:151, length:13, index:2})",
|
||||
"The left edge of the third script was interpreted correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") - 2).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") - 2).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The left outside of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") - 2).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") - 2).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The left outside of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") - 2).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") - 2).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The left outside of the third script was interpreted correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") + 12).toSource(), "({start:31, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") + 12).toSource(), "({start:31, length:13, index:0})",
|
||||
"The right edge of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") + 12).toSource(), "({start:85, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") + 12).toSource(), "({start:85, length:13, index:1})",
|
||||
"The right edge of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") + 12).toSource(), "({start:151, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") + 12).toSource(), "({start:151, length:13, index:2})",
|
||||
"The right edge of the third script was interpreted correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") + 13).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") + 13).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The right outside of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") + 13).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") + 13).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The right outside of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") + 13).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") + 13).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The right outside of the third script was interpreted correctly.");
|
||||
|
||||
finish();
|
||||
|
@ -43,11 +43,11 @@ function test() {
|
||||
is(parsed.scriptCount, 1,
|
||||
"There should be 1 script parsed in the parent HTML source.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The first script shouldn't be considered valid.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13, index:0})",
|
||||
"The second script was located correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:-1, length:-1})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:-1, length:-1, index:-1})",
|
||||
"The third script shouldn't be considered valid.");
|
||||
|
||||
finish();
|
||||
|
@ -32,11 +32,11 @@ function test() {
|
||||
is(parsed.scriptCount, 1,
|
||||
"There should be 1 script parsed in the parent source.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:0, length:261})",
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:0, length:261, index:0})",
|
||||
"The script location is correct (1).");
|
||||
is(parsed.getScriptInfo(source.indexOf("<script>")).toSource(), "({start:0, length:261})",
|
||||
is(parsed.getScriptInfo(source.indexOf("<script>")).toSource(), "({start:0, length:261, index:0})",
|
||||
"The script location is correct (2).");
|
||||
is(parsed.getScriptInfo(source.indexOf("</script>")).toSource(), "({start:0, length:261})",
|
||||
is(parsed.getScriptInfo(source.indexOf("</script>")).toSource(), "({start:0, length:261, index:0})",
|
||||
"The script location is correct (3).");
|
||||
|
||||
finish();
|
||||
|
@ -51,14 +51,14 @@ function initialChecks() {
|
||||
is(gVariables.getScopeAtIndex(1).target, scopeNodes[1],
|
||||
"getScopeAtIndex(1) didn't return the expected scope.");
|
||||
|
||||
is(gVariables.getScopeForNode(scopeNodes[0]).target, scopeNodes[0],
|
||||
"getScopeForNode([0]) didn't return the expected scope.");
|
||||
is(gVariables.getScopeForNode(scopeNodes[1]).target, scopeNodes[1],
|
||||
"getScopeForNode([1]) didn't return the expected scope.");
|
||||
is(gVariables.getItemForNode(scopeNodes[0]).target, scopeNodes[0],
|
||||
"getItemForNode([0]) didn't return the expected scope.");
|
||||
is(gVariables.getItemForNode(scopeNodes[1]).target, scopeNodes[1],
|
||||
"getItemForNode([1]) didn't return the expected scope.");
|
||||
|
||||
is(gVariables.getScopeForNode(scopeNodes[0]).expanded, true,
|
||||
is(gVariables.getItemForNode(scopeNodes[0]).expanded, true,
|
||||
"The local scope should be expanded by default.");
|
||||
is(gVariables.getScopeForNode(scopeNodes[1]).expanded, false,
|
||||
is(gVariables.getItemForNode(scopeNodes[1]).expanded, false,
|
||||
"The global scope should not be collapsed by default.");
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,219 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that VariablesView methods responsible for styling variables
|
||||
* as overridden work properly.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_scope-variable-2.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let variables = win.DebuggerView.Variables;
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
|
||||
|
||||
let firstScope = variables.getScopeAtIndex(0);
|
||||
let secondScope = variables.getScopeAtIndex(1);
|
||||
let thirdScope = variables.getScopeAtIndex(2);
|
||||
let globalScope = variables.getScopeAtIndex(3);
|
||||
|
||||
ok(firstScope, "The first scope is available.");
|
||||
ok(secondScope, "The second scope is available.");
|
||||
ok(thirdScope, "The third scope is available.");
|
||||
ok(globalScope, "The global scope is available.");
|
||||
|
||||
is(firstScope.name, "Function scope [secondNest]",
|
||||
"The first scope's name is correct.");
|
||||
is(secondScope.name, "Function scope [firstNest]",
|
||||
"The second scope's name is correct.");
|
||||
is(thirdScope.name, "Function scope [test]",
|
||||
"The third scope's name is correct.");
|
||||
is(globalScope.name, "Global scope [Window]",
|
||||
"The global scope's name is correct.");
|
||||
|
||||
is(firstScope.expanded, true,
|
||||
"The first scope's expansion state is correct.");
|
||||
is(secondScope.expanded, false,
|
||||
"The second scope's expansion state is correct.");
|
||||
is(thirdScope.expanded, false,
|
||||
"The third scope's expansion state is correct.");
|
||||
is(globalScope.expanded, false,
|
||||
"The global scope's expansion state is correct.");
|
||||
|
||||
is(firstScope._store.size, 3,
|
||||
"The first scope should have all the variables available.");
|
||||
is(secondScope._store.size, 3,
|
||||
"The second scope shoild have all the variables available.");
|
||||
is(thirdScope._store.size, 3,
|
||||
"The third scope shoild have all the variables available.");
|
||||
is(globalScope._store.size, 0,
|
||||
"The global scope shoild have no variables available.");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with simple variables.
|
||||
|
||||
let thisVar = firstScope.get("this");
|
||||
let thisOwner = variables.getOwnerScopeForVariableOrProperty(thisVar);
|
||||
is(thisOwner, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (1).");
|
||||
|
||||
let someVar1 = firstScope.get("a");
|
||||
let someOwner1 = variables.getOwnerScopeForVariableOrProperty(someVar1);
|
||||
is(someOwner1, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (2).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with first-degree properties.
|
||||
|
||||
let argsVar1 = firstScope.get("arguments");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
argsVar1.expand();
|
||||
yield fetched;
|
||||
|
||||
let calleeProp1 = argsVar1.get("callee");
|
||||
let calleeOwner1 = variables.getOwnerScopeForVariableOrProperty(calleeProp1);
|
||||
is(calleeOwner1, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (3).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with second-degree properties.
|
||||
|
||||
let protoVar1 = argsVar1.get("__proto__");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
protoVar1.expand();
|
||||
yield fetched;
|
||||
|
||||
let constrProp1 = protoVar1.get("constructor");
|
||||
let constrOwner1 = variables.getOwnerScopeForVariableOrProperty(constrProp1);
|
||||
is(constrOwner1, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (4).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with a simple variable
|
||||
// from non-topmost scopes.
|
||||
|
||||
let someVar2 = secondScope.get("a");
|
||||
let someOwner2 = variables.getOwnerScopeForVariableOrProperty(someVar2);
|
||||
is(someOwner2, secondScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (5).");
|
||||
|
||||
let someVar3 = thirdScope.get("a");
|
||||
let someOwner3 = variables.getOwnerScopeForVariableOrProperty(someVar3);
|
||||
is(someOwner3, thirdScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (6).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with first-degree properies
|
||||
// from non-topmost scopes.
|
||||
|
||||
let argsVar2 = secondScope.get("arguments");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
argsVar2.expand();
|
||||
yield fetched;
|
||||
|
||||
let calleeProp2 = argsVar2.get("callee");
|
||||
let calleeOwner2 = variables.getOwnerScopeForVariableOrProperty(calleeProp2);
|
||||
is(calleeOwner2, secondScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (7).");
|
||||
|
||||
let argsVar3 = thirdScope.get("arguments");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
argsVar3.expand();
|
||||
yield fetched;
|
||||
|
||||
let calleeProp3 = argsVar3.get("callee");
|
||||
let calleeOwner3 = variables.getOwnerScopeForVariableOrProperty(calleeProp3);
|
||||
is(calleeOwner3, thirdScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (8).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with second-degree properties
|
||||
// from non-topmost scopes.
|
||||
|
||||
let protoVar2 = argsVar2.get("__proto__");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
protoVar2.expand();
|
||||
yield fetched;
|
||||
|
||||
let constrProp2 = protoVar2.get("constructor");
|
||||
let constrOwner2 = variables.getOwnerScopeForVariableOrProperty(constrProp2);
|
||||
is(constrOwner2, secondScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (9).");
|
||||
|
||||
let protoVar3 = argsVar3.get("__proto__");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
protoVar3.expand();
|
||||
yield fetched;
|
||||
|
||||
let constrProp3 = protoVar3.get("constructor");
|
||||
let constrOwner3 = variables.getOwnerScopeForVariableOrProperty(constrProp3);
|
||||
is(constrOwner3, thirdScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (10).");
|
||||
|
||||
// Test getParentScopesForVariableOrProperty with simple variables.
|
||||
|
||||
let varOwners1 = variables.getParentScopesForVariableOrProperty(someVar1);
|
||||
let varOwners2 = variables.getParentScopesForVariableOrProperty(someVar2);
|
||||
let varOwners3 = variables.getParentScopesForVariableOrProperty(someVar3);
|
||||
|
||||
is(varOwners1.length, 0,
|
||||
"There should be no owner scopes for the first variable.");
|
||||
|
||||
is(varOwners2.length, 1,
|
||||
"There should be one owner scope for the second variable.");
|
||||
is(varOwners2[0], firstScope,
|
||||
"The only owner scope for the second variable is correct.");
|
||||
|
||||
is(varOwners3.length, 2,
|
||||
"There should be two owner scopes for the third variable.");
|
||||
is(varOwners3[0], firstScope,
|
||||
"The first owner scope for the third variable is correct.");
|
||||
is(varOwners3[1], secondScope,
|
||||
"The second owner scope for the third variable is correct.");
|
||||
|
||||
// Test getParentScopesForVariableOrProperty with first-degree properties.
|
||||
|
||||
let propOwners1 = variables.getParentScopesForVariableOrProperty(calleeProp1);
|
||||
let propOwners2 = variables.getParentScopesForVariableOrProperty(calleeProp2);
|
||||
let propOwners3 = variables.getParentScopesForVariableOrProperty(calleeProp3);
|
||||
|
||||
is(propOwners1.length, 0,
|
||||
"There should be no owner scopes for the first property.");
|
||||
|
||||
is(propOwners2.length, 1,
|
||||
"There should be one owner scope for the second property.");
|
||||
is(propOwners2[0], firstScope,
|
||||
"The only owner scope for the second property is correct.");
|
||||
|
||||
is(propOwners3.length, 2,
|
||||
"There should be two owner scopes for the third property.");
|
||||
is(propOwners3[0], firstScope,
|
||||
"The first owner scope for the third property is correct.");
|
||||
is(propOwners3[1], secondScope,
|
||||
"The second owner scope for the third property is correct.");
|
||||
|
||||
// Test getParentScopesForVariableOrProperty with second-degree properties.
|
||||
|
||||
let secPropOwners1 = variables.getParentScopesForVariableOrProperty(constrProp1);
|
||||
let secPropOwners2 = variables.getParentScopesForVariableOrProperty(constrProp2);
|
||||
let secPropOwners3 = variables.getParentScopesForVariableOrProperty(constrProp3);
|
||||
|
||||
is(secPropOwners1.length, 0,
|
||||
"There should be no owner scopes for the first inner property.");
|
||||
|
||||
is(secPropOwners2.length, 1,
|
||||
"There should be one owner scope for the second inner property.");
|
||||
is(secPropOwners2[0], firstScope,
|
||||
"The only owner scope for the second inner property is correct.");
|
||||
|
||||
is(secPropOwners3.length, 2,
|
||||
"There should be two owner scopes for the third inner property.");
|
||||
is(secPropOwners3[0], firstScope,
|
||||
"The first owner scope for the third inner property is correct.");
|
||||
is(secPropOwners3[1], secondScope,
|
||||
"The second owner scope for the third inner property is correct.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that overridden variables in the VariablesView are styled properly.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_scope-variable-2.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let variables = win.DebuggerView.Variables;
|
||||
|
||||
// Wait for the hierarchy to be committed by the VariablesViewController.
|
||||
let committed = promise.defer();
|
||||
variables.oncommit = committed.resolve;
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
|
||||
yield committed.promise;
|
||||
|
||||
let firstScope = variables.getScopeAtIndex(0);
|
||||
let secondScope = variables.getScopeAtIndex(1);
|
||||
let thirdScope = variables.getScopeAtIndex(2);
|
||||
|
||||
let someVar1 = firstScope.get("a");
|
||||
let someVar2 = secondScope.get("a");
|
||||
let someVar3 = thirdScope.get("a");
|
||||
|
||||
let argsVar1 = firstScope.get("arguments");
|
||||
let argsVar2 = secondScope.get("arguments");
|
||||
let argsVar3 = thirdScope.get("arguments");
|
||||
|
||||
is(someVar1.target.hasAttribute("overridden"), false,
|
||||
"The first 'a' variable should not be marked as being overridden.");
|
||||
is(someVar2.target.hasAttribute("overridden"), true,
|
||||
"The second 'a' variable should be marked as being overridden.");
|
||||
is(someVar3.target.hasAttribute("overridden"), true,
|
||||
"The third 'a' variable should be marked as being overridden.");
|
||||
|
||||
is(argsVar1.target.hasAttribute("overridden"), false,
|
||||
"The first 'arguments' variable should not be marked as being overridden.");
|
||||
is(argsVar2.target.hasAttribute("overridden"), true,
|
||||
"The second 'arguments' variable should be marked as being overridden.");
|
||||
is(argsVar3.target.hasAttribute("overridden"), true,
|
||||
"The third 'arguments' variable should be marked as being overridden.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests opening inspecting variables works across scopes.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_scope-variable-3.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 15);
|
||||
|
||||
yield openVarPopup(panel, { line: 12, ch: 10 });
|
||||
ok(true, "The variable inspection popup was shown for the real variable.");
|
||||
|
||||
once(tooltip, "popupshown").then(() => {
|
||||
ok(false, "The variable inspection popup shouldn't have been opened.");
|
||||
});
|
||||
|
||||
reopenVarPopup(panel, { line: 18, ch: 10 });
|
||||
yield waitForTime(1000);
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -23,6 +23,9 @@ function test() {
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gVariables = gDebugger.DebuggerView.Variables;
|
||||
|
||||
// Always expand all items between pauses except 'window' variables.
|
||||
gVariables.commitHierarchyIgnoredItems = Object.create(null, { window: { value: true } });
|
||||
|
||||
waitForSourceShown(gPanel, ".html")
|
||||
.then(addBreakpoint)
|
||||
.then(() => ensureThreadClientState(gPanel, "resumed"))
|
||||
|
@ -24,7 +24,7 @@ function test() {
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gVariables = gDebugger.DebuggerView.Variables;
|
||||
|
||||
// Always expand all scopes between pauses.
|
||||
// Always expand all items between pauses.
|
||||
gVariables.commitHierarchyIgnoredItems = Object.create(null);
|
||||
|
||||
waitForSourceShown(gPanel, ".html")
|
||||
|
30
browser/devtools/debugger/test/doc_scope-variable-2.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function test() {
|
||||
var a = "first scope";
|
||||
firstNest();
|
||||
|
||||
function firstNest() {
|
||||
var a = "second scope";
|
||||
secondNest();
|
||||
|
||||
function secondNest() {
|
||||
var a = "third scope";
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
23
browser/devtools/debugger/test/doc_scope-variable-3.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var trap = "first script";
|
||||
function test() {
|
||||
debugger;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript">/*
|
||||
trololol
|
||||
*/</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1173,10 +1173,11 @@ MarkupContainer.prototype = {
|
||||
this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => {
|
||||
if (data) {
|
||||
data.data.string().then(str => {
|
||||
let res = {data: str, size: data.size};
|
||||
// Resolving the data promise and, to always keep tooltipData.data
|
||||
// as a promise, create a new one that resolves immediately
|
||||
def.resolve(str, data.size);
|
||||
this.tooltipData.data = promise.resolve(str, data.size);
|
||||
def.resolve(res);
|
||||
this.tooltipData.data = promise.resolve(res);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1186,7 +1187,7 @@ MarkupContainer.prototype = {
|
||||
|
||||
_buildTooltipContent: function(target, tooltip) {
|
||||
if (this.tooltipData && target === this.tooltipData.target) {
|
||||
this.tooltipData.data.then((data, size) => {
|
||||
this.tooltipData.data.then(({data, size}) => {
|
||||
tooltip.setImageContent(data, size);
|
||||
});
|
||||
return true;
|
||||
|
@ -16,10 +16,10 @@ const PAGE_CONTENT = [
|
||||
].join("\n");
|
||||
|
||||
const TEST_NODES = [
|
||||
"img.local",
|
||||
"img.data",
|
||||
"img.remote",
|
||||
".canvas"
|
||||
{selector: "img.local", size: "192 x 192"},
|
||||
{selector: "img.data", size: "64 x 64"},
|
||||
{selector: "img.remote", size: "22 x 23"},
|
||||
{selector: ".canvas", size: "600 x 600"}
|
||||
];
|
||||
|
||||
function test() {
|
||||
@ -77,8 +77,8 @@ function testImageTooltip(index) {
|
||||
return endTests();
|
||||
}
|
||||
|
||||
let node = contentDoc.querySelector(TEST_NODES[index]);
|
||||
ok(node, "We have the [" + TEST_NODES[index] + "] image node to test for tooltip");
|
||||
let node = contentDoc.querySelector(TEST_NODES[index].selector);
|
||||
ok(node, "We have the [" + TEST_NODES[index].selector + "] image node to test for tooltip");
|
||||
let isImg = node.tagName.toLowerCase() === "img";
|
||||
|
||||
let container = getContainerForRawNode(markup, node);
|
||||
@ -90,10 +90,14 @@ function testImageTooltip(index) {
|
||||
|
||||
assertTooltipShownOn(target, () => {
|
||||
let images = markup.tooltip.panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image");
|
||||
is(images.length, 1,
|
||||
"Tooltip for [" + TEST_NODES[index].selector + "] contains an image");
|
||||
|
||||
let label = markup.tooltip.panel.querySelector(".devtools-tooltip-caption");
|
||||
is(label.textContent, TEST_NODES[index].size,
|
||||
"Tooltip label for [" + TEST_NODES[index].selector + "] displays the right image size")
|
||||
|
||||
markup.tooltip.hide();
|
||||
|
||||
testImageTooltip(index + 1);
|
||||
});
|
||||
}
|
||||
|
@ -132,15 +132,15 @@ SyntaxTreesPool.prototype = {
|
||||
/**
|
||||
* @see SyntaxTree.prototype.getIdentifierAt
|
||||
*/
|
||||
getIdentifierAt: function(aLine, aColumn) {
|
||||
return this._first(this._call("getIdentifierAt", aLine, aColumn));
|
||||
getIdentifierAt: function({ line, column, scriptIndex }) {
|
||||
return this._first(this._call("getIdentifierAt", scriptIndex, line, column));
|
||||
},
|
||||
|
||||
/**
|
||||
* @see SyntaxTree.prototype.getNamedFunctionDefinitions
|
||||
*/
|
||||
getNamedFunctionDefinitions: function(aSubstring) {
|
||||
return this._call("getNamedFunctionDefinitions", aSubstring);
|
||||
return this._call("getNamedFunctionDefinitions", -1, aSubstring);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -161,12 +161,19 @@ SyntaxTreesPool.prototype = {
|
||||
* The offset and length relative to the enclosing script.
|
||||
*/
|
||||
getScriptInfo: function(aOffset) {
|
||||
let info = { start: -1, length: -1, index: -1 };
|
||||
|
||||
for (let { offset, length } of this._trees) {
|
||||
if (offset <= aOffset && offset + length >= aOffset) {
|
||||
return { start: offset, length: length };
|
||||
info.index++;
|
||||
if (offset <= aOffset && offset + length >= aOffset) {
|
||||
info.start = offset;
|
||||
info.length = length;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return { start: -1, length: -1 };
|
||||
|
||||
info.index = -1;
|
||||
return info;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -182,23 +189,31 @@ SyntaxTreesPool.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles a request for all known syntax trees.
|
||||
* Handles a request for a specific or all known syntax trees.
|
||||
*
|
||||
* @param string aFunction
|
||||
* The function name to call on the SyntaxTree instances.
|
||||
* @param number aSyntaxTreeIndex
|
||||
* The syntax tree for which to handle the request. If the tree at
|
||||
* the specified index isn't found, the accumulated results for all
|
||||
* syntax trees are returned.
|
||||
* @param any aParams
|
||||
* Any kind params to pass to the request function.
|
||||
* @return array
|
||||
* The results given by all known syntax trees.
|
||||
*/
|
||||
_call: function(aFunction, ...aParams) {
|
||||
_call: function(aFunction, aSyntaxTreeIndex, ...aParams) {
|
||||
let results = [];
|
||||
let requestId = aFunction + aParams.toSource(); // Cache all the things!
|
||||
let requestId = [aFunction, aSyntaxTreeIndex, aParams].toSource();
|
||||
|
||||
if (this._cache.has(requestId)) {
|
||||
return this._cache.get(requestId);
|
||||
}
|
||||
for (let syntaxTree of this._trees) {
|
||||
|
||||
let requestedTree = this._trees[aSyntaxTreeIndex];
|
||||
let targettedTrees = requestedTree ? [requestedTree] : this._trees;
|
||||
|
||||
for (let syntaxTree of targettedTrees) {
|
||||
try {
|
||||
results.push({
|
||||
sourceUrl: syntaxTree.url,
|
||||
|
@ -518,30 +518,33 @@ Tooltip.prototype = {
|
||||
}
|
||||
vbox.appendChild(image);
|
||||
|
||||
// Temporary label during image load
|
||||
// Dimension label
|
||||
let label = this.doc.createElement("label");
|
||||
label.classList.add("devtools-tooltip-caption");
|
||||
label.classList.add("theme-comment");
|
||||
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
|
||||
if (options.naturalWidth && options.naturalHeight) {
|
||||
label.textContent = this._getImageDimensionLabel(options.naturalWidth,
|
||||
options.naturalHeight);
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
} else {
|
||||
// If no dimensions were provided, load the image to get them
|
||||
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
|
||||
let imgObj = new this.doc.defaultView.Image();
|
||||
imgObj.src = imageUrl;
|
||||
imgObj.onload = () => {
|
||||
imgObj.onload = null;
|
||||
label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth,
|
||||
imgObj.naturalHeight);
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
}
|
||||
}
|
||||
vbox.appendChild(label);
|
||||
|
||||
this.content = vbox;
|
||||
|
||||
// Load the image to get dimensions and display it when done
|
||||
let imgObj = new this.doc.defaultView.Image();
|
||||
imgObj.src = imageUrl;
|
||||
imgObj.onload = () => {
|
||||
imgObj.onload = null;
|
||||
|
||||
// Display dimensions
|
||||
let w = options.naturalWidth || imgObj.naturalWidth;
|
||||
let h = options.naturalHeight || imgObj.naturalHeight;
|
||||
label.textContent = w + " x " + h;
|
||||
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
}
|
||||
},
|
||||
|
||||
_getImageDimensionLabel: (w, h) => w + " x " + h,
|
||||
|
||||
/**
|
||||
* Exactly the same as the `image` function but takes a css background image
|
||||
* value instead : url(....)
|
||||
|
@ -16,6 +16,7 @@ const LAZY_APPEND_BATCH = 100; // nodes
|
||||
const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
|
||||
const PAGE_SIZE_MAX_JUMPS = 30;
|
||||
const SEARCH_ACTION_MAX_DELAY = 300; // ms
|
||||
const ITEM_FLASH_DURATION = 300 // ms
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
@ -117,6 +118,9 @@ VariablesView.prototype = {
|
||||
/**
|
||||
* Adds a scope to contain any inspected variables.
|
||||
*
|
||||
* This new scope will be considered the parent of any other scope
|
||||
* added afterwards.
|
||||
*
|
||||
* @param string aName
|
||||
* The scope's name (e.g. "Local", "Global" etc.).
|
||||
* @return Scope
|
||||
@ -131,6 +135,7 @@ VariablesView.prototype = {
|
||||
this._itemsByElement.set(scope._target, scope);
|
||||
this._currHierarchy.set(aName, scope);
|
||||
scope.header = !!aName;
|
||||
|
||||
return scope;
|
||||
},
|
||||
|
||||
@ -604,23 +609,6 @@ VariablesView.prototype = {
|
||||
return this._store[aIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for the scope in this container displayed by the specified node.
|
||||
*
|
||||
* @param nsIDOMNode aNode
|
||||
* The node to search for.
|
||||
* @return Scope
|
||||
* The matched scope, or null if nothing is found.
|
||||
*/
|
||||
getScopeForNode: function(aNode) {
|
||||
let item = this._itemsByElement.get(aNode);
|
||||
// Match only Scopes, not Variables or Properties.
|
||||
if (item && !(item instanceof Variable)) {
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively searches this container for the scope, variable or property
|
||||
* displayed by the specified node.
|
||||
@ -634,6 +622,43 @@ VariablesView.prototype = {
|
||||
return this._itemsByElement.get(aNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the scope owning a Variable or Property.
|
||||
*
|
||||
* @param Variable | Property
|
||||
* The variable or property to retrieven the owner scope for.
|
||||
* @return Scope
|
||||
* The owner scope.
|
||||
*/
|
||||
getOwnerScopeForVariableOrProperty: function(aItem) {
|
||||
if (!aItem) {
|
||||
return null;
|
||||
}
|
||||
// If this is a Scope, return it.
|
||||
if (!(aItem instanceof Variable)) {
|
||||
return aItem;
|
||||
}
|
||||
// If this is a Variable or Property, find its owner scope.
|
||||
if (aItem instanceof Variable && aItem.ownerView) {
|
||||
return this.getOwnerScopeForVariableOrProperty(aItem.ownerView);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the parent scopes for a specified Variable or Property.
|
||||
* The returned list will not include the owner scope.
|
||||
*
|
||||
* @param Variable | Property
|
||||
* The variable or property for which to find the parent scopes.
|
||||
* @return array
|
||||
* A list of parent Scopes.
|
||||
*/
|
||||
getParentScopesForVariableOrProperty: function(aItem) {
|
||||
let scope = this.getOwnerScopeForVariableOrProperty(aItem);
|
||||
return this._store.slice(0, Math.max(this._store.indexOf(scope), 0));
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the currently focused scope, variable or property in this view.
|
||||
*
|
||||
@ -888,6 +913,7 @@ VariablesView.prototype = {
|
||||
label.className = "variables-view-empty-notice";
|
||||
label.setAttribute("value", this._emptyTextValue);
|
||||
|
||||
this._parent.setAttribute("empty", "");
|
||||
this._parent.appendChild(label);
|
||||
this._emptyTextNode = label;
|
||||
},
|
||||
@ -900,6 +926,7 @@ VariablesView.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._parent.removeAttribute("empty");
|
||||
this._parent.removeChild(this._emptyTextNode);
|
||||
this._emptyTextNode = null;
|
||||
},
|
||||
@ -976,12 +1003,15 @@ VariablesView.prototype = {
|
||||
_window: null,
|
||||
|
||||
_store: null,
|
||||
_itemsByElement: null,
|
||||
_prevHierarchy: null,
|
||||
_currHierarchy: null,
|
||||
|
||||
_enumVisible: true,
|
||||
_nonEnumVisible: true,
|
||||
_alignedValues: false,
|
||||
_actionsFirst: false,
|
||||
|
||||
_parent: null,
|
||||
_list: null,
|
||||
_searchboxNode: null,
|
||||
@ -1244,6 +1274,7 @@ Scope.prototype = {
|
||||
this._variablesView._itemsByElement.set(child._target, child);
|
||||
this._variablesView._currHierarchy.set(child._absoluteName, child);
|
||||
child.header = !!aName;
|
||||
|
||||
return child;
|
||||
},
|
||||
|
||||
@ -1294,7 +1325,9 @@ Scope.prototype = {
|
||||
view._store.splice(view._store.indexOf(this), 1);
|
||||
view._itemsByElement.delete(this._target);
|
||||
view._currHierarchy.delete(this._nameString);
|
||||
|
||||
this._target.remove();
|
||||
|
||||
for (let variable of this._store.values()) {
|
||||
variable.remove();
|
||||
}
|
||||
@ -1725,7 +1758,8 @@ Scope.prototype = {
|
||||
_onClick: function(e) {
|
||||
if (e.button != 0 ||
|
||||
e.target == this._editNode ||
|
||||
e.target == this._deleteNode) {
|
||||
e.target == this._deleteNode ||
|
||||
e.target == this._addPropertyNode) {
|
||||
return;
|
||||
}
|
||||
this.toggle();
|
||||
@ -1935,25 +1969,6 @@ Scope.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the first search results match in this scope.
|
||||
* @return Variable | Property
|
||||
*/
|
||||
get _firstMatch() {
|
||||
for (let [, variable] of this._store) {
|
||||
let match;
|
||||
if (variable._isMatch) {
|
||||
match = variable;
|
||||
} else {
|
||||
match = variable._firstMatch;
|
||||
}
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the first item in the tree of visible items in this item that matches
|
||||
* the predicate. Searches in visual order (the order seen by the user).
|
||||
@ -2078,11 +2093,12 @@ Scope.prototype = {
|
||||
switch: null,
|
||||
delete: null,
|
||||
new: null,
|
||||
editableValueTooltip: "",
|
||||
preventDisableOnChange: false,
|
||||
preventDescriptorModifiers: false,
|
||||
editableNameTooltip: "",
|
||||
editableValueTooltip: "",
|
||||
editButtonTooltip: "",
|
||||
deleteButtonTooltip: "",
|
||||
preventDescriptorModifiers: false,
|
||||
contextMenuId: "",
|
||||
separatorStr: "",
|
||||
|
||||
@ -2178,7 +2194,9 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this.ownerView._store.delete(this._nameString);
|
||||
this._variablesView._itemsByElement.delete(this._target);
|
||||
this._variablesView._currHierarchy.delete(this._absoluteName);
|
||||
|
||||
this._target.remove();
|
||||
|
||||
for (let property of this._store.values()) {
|
||||
property.remove();
|
||||
}
|
||||
@ -2355,6 +2373,37 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this._valueLabel.setAttribute("value", this._valueString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Marks this variable as overridden.
|
||||
*
|
||||
* @param boolean aFlag
|
||||
* Whether this variable is overridden or not.
|
||||
*/
|
||||
setOverridden: function(aFlag) {
|
||||
if (aFlag) {
|
||||
this._target.setAttribute("overridden", "");
|
||||
} else {
|
||||
this._target.removeAttribute("overridden");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Briefly flashes this variable.
|
||||
*
|
||||
* @param number aDuration [optional]
|
||||
* An optional flash animation duration.
|
||||
*/
|
||||
flash: function(aDuration = ITEM_FLASH_DURATION) {
|
||||
let fadeInDelay = this._variablesView.lazyEmptyDelay + 1;
|
||||
let fadeOutDelay = fadeInDelay + aDuration;
|
||||
|
||||
setNamedTimeout("vview-flash-in" + this._absoluteName,
|
||||
fadeInDelay, () => this._target.setAttribute("changed", ""));
|
||||
|
||||
setNamedTimeout("vview-flash-out" + this._absoluteName,
|
||||
fadeOutDelay, () => this._target.removeAttribute("changed"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes this variable's id, view and binds event listeners.
|
||||
*
|
||||
@ -2405,7 +2454,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
let separatorLabel = this._separatorLabel = document.createElement("label");
|
||||
separatorLabel.className = "plain separator";
|
||||
separatorLabel.setAttribute("value", this.ownerView.separatorStr);
|
||||
separatorLabel.setAttribute("value", this.ownerView.separatorStr + " ");
|
||||
|
||||
let valueLabel = this._valueLabel = document.createElement("label");
|
||||
valueLabel.className = "plain value";
|
||||
@ -2428,6 +2477,8 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
separatorLabel.hidden = true;
|
||||
}
|
||||
|
||||
// If this is a getter/setter property, create two child pseudo-properties
|
||||
// called "get" and "set" that display the corresponding functions.
|
||||
if (descriptor.get || descriptor.set) {
|
||||
separatorLabel.hidden = true;
|
||||
valueLabel.hidden = true;
|
||||
@ -2480,15 +2531,16 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this._title.appendChild(deleteNode);
|
||||
}
|
||||
|
||||
let { actionsFirst } = this._variablesView;
|
||||
if (ownerView.new || actionsFirst) {
|
||||
if (ownerView.new) {
|
||||
let addPropertyNode = this._addPropertyNode = this.document.createElement("toolbarbutton");
|
||||
addPropertyNode.className = "plain variables-view-add-property";
|
||||
addPropertyNode.addEventListener("mousedown", this._onAddProperty.bind(this), false);
|
||||
if (actionsFirst && VariablesView.isPrimitive(descriptor)) {
|
||||
this._title.appendChild(addPropertyNode);
|
||||
|
||||
// Can't add properties to primitive values, hide the node in those cases.
|
||||
if (VariablesView.isPrimitive(descriptor)) {
|
||||
addPropertyNode.setAttribute("invisible", "");
|
||||
}
|
||||
this._title.appendChild(addPropertyNode);
|
||||
}
|
||||
|
||||
if (ownerView.contextMenuId) {
|
||||
@ -2554,11 +2606,12 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
let labels = [
|
||||
"configurable", "enumerable", "writable",
|
||||
"frozen", "sealed", "extensible", "WebIDL"];
|
||||
"frozen", "sealed", "extensible", "overridden", "WebIDL"];
|
||||
|
||||
for (let label of labels) {
|
||||
for (let type of labels) {
|
||||
let labelElement = this.document.createElement("label");
|
||||
labelElement.setAttribute("value", label);
|
||||
labelElement.className = type;
|
||||
labelElement.setAttribute("value", STR.GetStringFromName(type + "Tooltip"));
|
||||
tooltip.appendChild(labelElement);
|
||||
}
|
||||
|
||||
@ -2623,17 +2676,25 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
if (descriptor && "getterValue" in descriptor) {
|
||||
target.setAttribute("safe-getter", "");
|
||||
}
|
||||
|
||||
if (name == "this") {
|
||||
target.setAttribute("self", "");
|
||||
}
|
||||
else if (name == "<exception>") {
|
||||
target.setAttribute("exception", "");
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
else if (name == "<return>") {
|
||||
target.setAttribute("return", "");
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
else if (name == "__proto__") {
|
||||
target.setAttribute("proto", "");
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
|
||||
if (Object.keys(descriptor).length == 0) {
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
},
|
||||
|
||||
@ -2774,8 +2835,8 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
_absoluteName: "",
|
||||
_initialDescriptor: null,
|
||||
_separatorLabel: null,
|
||||
_spacer: null,
|
||||
_valueLabel: null,
|
||||
_spacer: null,
|
||||
_editNode: null,
|
||||
_deleteNode: null,
|
||||
_addPropertyNode: null,
|
||||
@ -2877,69 +2938,101 @@ VariablesView.prototype.createHierarchy = function() {
|
||||
* scope/variable/property hierarchies and reopen previously expanded nodes.
|
||||
*/
|
||||
VariablesView.prototype.commitHierarchy = function() {
|
||||
let prevHierarchy = this._prevHierarchy;
|
||||
let currHierarchy = this._currHierarchy;
|
||||
|
||||
for (let [absoluteName, currVariable] of currHierarchy) {
|
||||
// Ignore variables which were already commmitted.
|
||||
if (currVariable._committed) {
|
||||
continue;
|
||||
}
|
||||
for (let [, currItem] of this._currHierarchy) {
|
||||
// Avoid performing expensive operations.
|
||||
if (this.commitHierarchyIgnoredItems[currVariable._nameString]) {
|
||||
if (this.commitHierarchyIgnoredItems[currItem._nameString]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to get the previous instance of the inspected variable to
|
||||
// determine the difference in state.
|
||||
let prevVariable = prevHierarchy.get(absoluteName);
|
||||
let expanded = false;
|
||||
let changed = false;
|
||||
|
||||
// If the inspected variable existed in a previous hierarchy, check if
|
||||
// the displayed value (a representation of the grip) has changed and if
|
||||
// it was previously expanded.
|
||||
if (prevVariable) {
|
||||
expanded = prevVariable._isExpanded;
|
||||
|
||||
// Only analyze Variables and Properties for displayed value changes.
|
||||
if (currVariable instanceof Variable) {
|
||||
changed = prevVariable._valueString != currVariable._valueString;
|
||||
}
|
||||
let overridden = this.isOverridden(currItem);
|
||||
if (overridden) {
|
||||
currItem.setOverridden(true);
|
||||
}
|
||||
|
||||
// Make sure this variable is not handled in ulteror commits for the
|
||||
// same hierarchy.
|
||||
currVariable._committed = true;
|
||||
|
||||
// Re-expand the variable if not previously collapsed.
|
||||
let expanded = !currItem._committed && this.wasExpanded(currItem);
|
||||
if (expanded) {
|
||||
currVariable.expand();
|
||||
currItem.expand();
|
||||
}
|
||||
// This variable was either not changed or removed, no need to continue.
|
||||
if (!changed) {
|
||||
continue;
|
||||
let changed = !currItem._committed && this.hasChanged(currItem);
|
||||
if (changed) {
|
||||
currItem.flash();
|
||||
}
|
||||
|
||||
// Apply an attribute determining the flash type and duration.
|
||||
// Dispatch this action after all the nodes have been drawn, so that
|
||||
// the transition efects can take place.
|
||||
this.window.setTimeout(function(aTarget) {
|
||||
aTarget.addEventListener("transitionend", function onEvent() {
|
||||
aTarget.removeEventListener("transitionend", onEvent, false);
|
||||
aTarget.removeAttribute("changed");
|
||||
}, false);
|
||||
aTarget.setAttribute("changed", "");
|
||||
}.bind(this, currVariable.target), this.lazyEmptyDelay + 1);
|
||||
currItem._committed = true;
|
||||
}
|
||||
if (this.oncommit) {
|
||||
this.oncommit(this);
|
||||
}
|
||||
};
|
||||
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It would be a bad idea to re-expand them or perform expensive operations.
|
||||
VariablesView.prototype.commitHierarchyIgnoredItems = Object.create(null, {
|
||||
"window": { value: true }
|
||||
VariablesView.prototype.commitHierarchyIgnoredItems = Heritage.extend(null, {
|
||||
"window": true,
|
||||
"this": true
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks if the an item was previously expanded, if it existed in a
|
||||
* previous hierarchy.
|
||||
*
|
||||
* @param Scope | Variable | Property aItem
|
||||
* The item to verify.
|
||||
* @return boolean
|
||||
* Whether the item was expanded.
|
||||
*/
|
||||
VariablesView.prototype.wasExpanded = function(aItem) {
|
||||
if (!(aItem instanceof Scope)) {
|
||||
return false;
|
||||
}
|
||||
let prevItem = this._prevHierarchy.get(aItem._absoluteName || aItem._nameString);
|
||||
return prevItem ? prevItem._isExpanded : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the an item's displayed value (a representation of the grip)
|
||||
* has changed, if it existed in a previous hierarchy.
|
||||
*
|
||||
* @param Variable | Property aItem
|
||||
* The item to verify.
|
||||
* @return boolean
|
||||
* Whether the item has changed.
|
||||
*/
|
||||
VariablesView.prototype.hasChanged = function(aItem) {
|
||||
// Only analyze Variables and Properties for displayed value changes.
|
||||
// Scopes are just collections of Variables and Properties and
|
||||
// don't have a "value", so they can't change.
|
||||
if (!(aItem instanceof Variable)) {
|
||||
return false;
|
||||
}
|
||||
let prevItem = this._prevHierarchy.get(aItem._absoluteName);
|
||||
return prevItem ? prevItem._valueString != aItem._valueString : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the an item was previously expanded, if it existed in a
|
||||
* previous hierarchy.
|
||||
*
|
||||
* @param Scope | Variable | Property aItem
|
||||
* The item to verify.
|
||||
* @return boolean
|
||||
* Whether the item was expanded.
|
||||
*/
|
||||
VariablesView.prototype.isOverridden = function(aItem) {
|
||||
// Only analyze Variables for being overridden in different Scopes.
|
||||
if (!(aItem instanceof Variable) || aItem instanceof Property) {
|
||||
return false;
|
||||
}
|
||||
let currVariableName = aItem._nameString;
|
||||
let parentScopes = this.getParentScopesForVariableOrProperty(aItem);
|
||||
|
||||
for (let otherScope of parentScopes) {
|
||||
for (let [otherVariableName] of otherScope) {
|
||||
if (otherVariableName == currVariableName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the descriptor represents an undefined, null or
|
||||
* primitive value.
|
||||
@ -3247,9 +3340,6 @@ Editable.prototype = {
|
||||
let input = this._input = this._variable.document.createElement("textbox");
|
||||
input.className = "plain " + this.className;
|
||||
input.setAttribute("value", initialString);
|
||||
if (!this._variable._variablesView.alignedValues) {
|
||||
input.setAttribute("flex", "1");
|
||||
}
|
||||
|
||||
// Replace the specified label with a textbox input element.
|
||||
label.parentNode.replaceChild(input, label);
|
||||
|
@ -352,13 +352,19 @@ VariablesViewController.prototype = {
|
||||
aTarget.showArrow();
|
||||
}
|
||||
|
||||
// Make sure that properties are always available on expansion.
|
||||
aTarget.onexpand = () => this.expand(aTarget, aSource);
|
||||
if (aSource.type == "block" || aSource.type == "function") {
|
||||
// Block and function environments already contain scope arguments and
|
||||
// corresponding variables as bindings.
|
||||
this.populate(aTarget, aSource);
|
||||
} else {
|
||||
// Make sure that properties are always available on expansion.
|
||||
aTarget.onexpand = () => this.populate(aTarget, aSource);
|
||||
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It's a good idea to be prepared in case of an expansion.
|
||||
if (aTarget.shouldPrefetch) {
|
||||
aTarget.addEventListener("mouseover", aTarget.onexpand, false);
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It's a good idea to be prepared in case of an expansion.
|
||||
if (aTarget.shouldPrefetch) {
|
||||
aTarget.addEventListener("mouseover", aTarget.onexpand, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Register all the actors that this controller now depends on.
|
||||
@ -373,6 +379,8 @@ VariablesViewController.prototype = {
|
||||
* Adds properties to a Scope, Variable, or Property in the view. Triggered
|
||||
* when a scope is expanded or certain variables are hovered.
|
||||
*
|
||||
* This does not expand the target, it only populates it.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope to be expanded.
|
||||
* @param object aSource
|
||||
@ -380,7 +388,7 @@ VariablesViewController.prototype = {
|
||||
* @return Promise
|
||||
* The promise that is resolved once the target has been expanded.
|
||||
*/
|
||||
expand: function(aTarget, aSource) {
|
||||
populate: function(aTarget, aSource) {
|
||||
// Fetch the variables only once.
|
||||
if (aTarget._fetched) {
|
||||
return aTarget._fetched;
|
||||
@ -510,16 +518,17 @@ VariablesViewController.prototype = {
|
||||
scope.locked = true; // Prevent collpasing the scope.
|
||||
|
||||
let variable = scope.addItem("", { enumerable: true });
|
||||
let expanded;
|
||||
let populated;
|
||||
|
||||
if (aOptions.objectActor) {
|
||||
expanded = this.expand(variable, aOptions.objectActor);
|
||||
populated = this.populate(variable, aOptions.objectActor);
|
||||
variable.expand();
|
||||
} else if (aOptions.rawObject) {
|
||||
variable.populate(aOptions.rawObject, { expanded: true });
|
||||
expanded = promise.resolve();
|
||||
populated = promise.resolve();
|
||||
}
|
||||
|
||||
return { variable: variable, expanded: expanded };
|
||||
return { variable: variable, expanded: populated };
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -56,10 +56,15 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.variable-or-property:not([safe-getter]) > tooltip > label[value=WebIDL],
|
||||
.variable-or-property:not([non-extensible]) > tooltip > label[value=extensible],
|
||||
.variable-or-property:not([frozen]) > tooltip > label[value=frozen],
|
||||
.variable-or-property:not([sealed]) > tooltip > label[value=sealed] {
|
||||
.variable-or-property:not([safe-getter]) > tooltip > label.WebIDL,
|
||||
.variable-or-property:not([overridden]) > tooltip > label.overridden,
|
||||
.variable-or-property:not([non-extensible]) > tooltip > label.extensible,
|
||||
.variable-or-property:not([frozen]) > tooltip > label.frozen,
|
||||
.variable-or-property:not([sealed]) > tooltip > label.sealed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.variable-or-property[pseudo-item] > tooltip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -73,6 +78,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.variables-view-container[aligned-values] .title > [optional-visibility] {
|
||||
.variables-view-container[aligned-values] [optional-visibility] {
|
||||
display: none;
|
||||
}
|
||||
|
@ -72,15 +72,26 @@ StyleEditorPanel.prototype = {
|
||||
*
|
||||
* @param {string} event
|
||||
* Type of event
|
||||
* @param {string} errorCode
|
||||
* @param {string} code
|
||||
* Error code of error to report
|
||||
* @param {string} message
|
||||
* Extra message to append to error message
|
||||
*/
|
||||
_showError: function(event, errorCode) {
|
||||
let message = _(errorCode);
|
||||
_showError: function(event, code, message) {
|
||||
if (!this._toolbox) {
|
||||
// could get an async error after we've been destroyed
|
||||
return;
|
||||
}
|
||||
|
||||
let errorMessage = _(code);
|
||||
if (message) {
|
||||
errorMessage += " " + message;
|
||||
}
|
||||
|
||||
let notificationBox = this._toolbox.getNotificationBox();
|
||||
let notification = notificationBox.getNotificationWithValue("styleeditor-error");
|
||||
if (!notification) {
|
||||
notificationBox.appendNotification(message,
|
||||
notificationBox.appendNotification(errorMessage,
|
||||
"styleeditor-error", "", notificationBox.PRIORITY_CRITICAL_LOW);
|
||||
}
|
||||
},
|
||||
|
@ -42,7 +42,7 @@ function highlightNode(aInspector, aComputedView)
|
||||
is(inspector.selection.node, div, "selection matches the div element");
|
||||
|
||||
expandProperty(0, testComputedViewLink);
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function testComputedViewLink() {
|
||||
|
@ -66,7 +66,7 @@ function highlightNode()
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div, "selection matches the div element");
|
||||
testInlineStyle();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function testInlineStyle()
|
||||
|
@ -48,7 +48,7 @@ function highlightNode()
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div, "selection matches the div element");
|
||||
testRuleViewLink();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function testRuleViewLink() {
|
||||
|
@ -49,11 +49,6 @@ can reach it easily. -->
|
||||
<!ENTITY bookmarkAllTabs.accesskey "T">
|
||||
<!ENTITY undoCloseTab.label "Undo Close Tab">
|
||||
<!ENTITY undoCloseTab.accesskey "U">
|
||||
<!-- LOCALIZATION NOTE (undoCloseTabs.label) : This label is used
|
||||
when the previous tab-closing operation closed more than one tab. It
|
||||
replaces the undoCloseTab.label and will use the same accesskey as the
|
||||
undoCloseTab.label so users will not need to learn new keyboard controls. -->
|
||||
<!ENTITY undoCloseTabs.label "Undo Close Tabs">
|
||||
<!ENTITY closeTab.label "Close Tab">
|
||||
<!ENTITY closeTab.accesskey "c">
|
||||
|
||||
|
@ -233,6 +233,23 @@ variablesCloseButtonTooltip=Click to remove
|
||||
# in the variables list on a getter or setter which can be edited.
|
||||
variablesEditButtonTooltip=Click to set value
|
||||
|
||||
# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed
|
||||
# in the variables list on certain variables or properties as tooltips.
|
||||
# Expanations of what these represent can be found at the following links:
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed
|
||||
# It's probably best to keep these in English.
|
||||
configurableTooltip=configurable
|
||||
enumerableTooltip=enumerable
|
||||
writableTooltip=writable
|
||||
frozenTooltip=frozen
|
||||
sealedTooltip=sealed
|
||||
extensibleTooltip=extensible
|
||||
overriddenTooltip=overridden
|
||||
WebIDLTooltip=WebIDL
|
||||
|
||||
# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed
|
||||
# in the variables list as a separator between the name and value.
|
||||
variablesSeparatorLabel=:
|
||||
|
@ -288,7 +288,7 @@ var ContextCommands = {
|
||||
viewPageSource: function cc_viewPageSource() {
|
||||
let uri = this.getPageSource();
|
||||
if (uri) {
|
||||
BrowserUI.addAndShowTab(uri);
|
||||
BrowserUI.addAndShowTab(uri, Browser.selectedTab);
|
||||
}
|
||||
},
|
||||
|
||||
|