Merge m-c to elm

This commit is contained in:
Nick Alexander 2013-12-16 09:13:10 -08:00
commit 163b1929e5
1080 changed files with 24467 additions and 14084 deletions

View File

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

View File

@ -692,7 +692,7 @@ getRoleCB(AtkObject *aAtkObj)
return aAtkObj->role;
}
AtkAttributeSet*
static AtkAttributeSet*
ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes)
{
if (!aAttributes)

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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]);

View File

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

View File

@ -1,4 +1,4 @@
{
"revision": "5bfef5faac50d14e055f642a44ed2df8483fb2fe",
"revision": "f112883f55f9f23abd5aac107ca09f5471c37798",
"repo_path": "/integration/gaia-central"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,5 +38,5 @@ function onTabViewWindowLoaded() {
gBrowser.removeTab(tabTwo);
finish();
});
}, 0);
});
}

View File

@ -64,7 +64,7 @@ function test() {
createBlankTab();
afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab);
}, 0);
});
});
}
@ -94,7 +94,7 @@ function test() {
gBrowser.removeTab(gBrowser.tabs[0]);
afterAllTabsLoaded(finishTest);
}, 0);
});
});
}

View File

@ -81,7 +81,7 @@ function test() {
gBrowser.removeTab(gBrowser.tabs[1]);
gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(finishTest);
}, 0);
});
}
waitForExplicitFinish();

View File

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

View File

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

View File

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

View File

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

View File

@ -53,3 +53,7 @@
#body[layout=horizontal] #vertical-layout-panes-container {
display: none;
}
#body[layout=vertical] #stackframes {
visibility: hidden;
}

View File

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

View File

@ -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).")
}
}

View File

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

View File

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

View File

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

View File

@ -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.");
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -288,7 +288,7 @@ var ContextCommands = {
viewPageSource: function cc_viewPageSource() {
let uri = this.getPageSource();
if (uri) {
BrowserUI.addAndShowTab(uri);
BrowserUI.addAndShowTab(uri, Browser.selectedTab);
}
},

Some files were not shown because too many files have changed in this diff Show More