зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central into services-central
This commit is contained in:
Коммит
9f7ac8b038
1
.hgtags
1
.hgtags
|
@ -72,3 +72,4 @@ c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
|
|||
462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
|
||||
54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
|
||||
a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220
|
||||
462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
|
||||
|
|
|
@ -616,6 +616,9 @@ nsAccessible::VisibilityState()
|
|||
} while (accessible = accessible->Parent());
|
||||
|
||||
nsIFrame* frame = GetFrame();
|
||||
if (!frame)
|
||||
return vstates;
|
||||
|
||||
const nsCOMPtr<nsIPresShell> shell(GetPresShell());
|
||||
|
||||
// We need to know if at least a kMinPixels around the object is visible,
|
||||
|
|
|
@ -1345,7 +1345,7 @@ nsHTMLTableAccessible::HasDescendant(const nsAString& aTagName,
|
|||
if (foundItemContent->GetChildCount() > 1)
|
||||
return true; // Treat multiple child nodes as non-empty
|
||||
|
||||
nsIContent *innerItemContent = foundItemContent->GetChildAt(0);
|
||||
nsIContent *innerItemContent = foundItemContent->GetFirstChild();
|
||||
if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
|
||||
return true;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ CMMSRCS = nsAccessNodeWrap.mm \
|
|||
mozDocAccessible.mm \
|
||||
mozActionElements.mm \
|
||||
mozTextAccessible.mm \
|
||||
mozHTMLAccessible.mm \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
||||
*/
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -15,11 +17,12 @@
|
|||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1999
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Original Author: Hub Figuière <hub@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -35,16 +38,8 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsXMLEncodingCID_h__
|
||||
#define nsXMLEncodingCID_h__
|
||||
#import "mozAccessible.h"
|
||||
|
||||
#include "nscore.h"
|
||||
@interface mozHeadingAccessible : mozAccessible
|
||||
|
||||
#define NS_XML_ENCODING_CONTRACTID "@mozilla.org/intl/xmlencoding;1"
|
||||
|
||||
// {12BB8F16-2389-11d3-B3BF-00805F8A6670}
|
||||
#define NS_XML_ENCODING_CID \
|
||||
{ 0x12bb8f16, 0x2389, 0x11d3, { 0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
|
||||
|
||||
|
||||
#endif // nsXMLEncodingCID_h__
|
||||
@end
|
|
@ -1,4 +1,6 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
||||
*/
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -15,11 +17,12 @@
|
|||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Original Author: Hub Figuière <hub@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -35,25 +38,19 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsDocumentCharsetInfo_h__
|
||||
#define nsDocumentCharsetInfo_h__
|
||||
#import "mozHTMLAccessible.h"
|
||||
|
||||
#include "nsIFactory.h"
|
||||
#include "nsIDocumentCharsetInfo.h"
|
||||
#import "nsHyperTextAccessible.h"
|
||||
|
||||
class nsDocumentCharsetInfo : public nsIDocumentCharsetInfo
|
||||
@implementation mozHeadingAccessible
|
||||
|
||||
- (id)value
|
||||
{
|
||||
public:
|
||||
nsDocumentCharsetInfo ();
|
||||
virtual ~nsDocumentCharsetInfo ();
|
||||
if (!mGeckoAccessible || !mGeckoAccessible->IsHyperText())
|
||||
return nil;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOCUMENTCHARSETINFO
|
||||
PRUint32 level = mGeckoAccessible->AsHyperText()->GetLevelInternal();
|
||||
return [NSNumber numberWithInt:level];
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAtom> mForcedCharset;
|
||||
nsCOMPtr<nsIAtom> mParentCharset;
|
||||
PRInt32 mParentCharsetSource;
|
||||
};
|
||||
|
||||
#endif // nsDocumentCharsetInfo_h__
|
||||
@end
|
|
@ -1,4 +1,6 @@
|
|||
#include "nsAccessibleWrap.h"
|
||||
|
||||
#include "nsCocoaUtils.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
#import "mozTextAccessible.h"
|
||||
|
@ -12,6 +14,7 @@ using namespace mozilla::a11y;
|
|||
- (long)textLength;
|
||||
- (BOOL)isReadOnly;
|
||||
- (void)setText:(NSString*)newText;
|
||||
- (NSString*)text;
|
||||
@end
|
||||
|
||||
@implementation mozTextAccessible
|
||||
|
@ -79,8 +82,9 @@ using namespace mozilla::a11y;
|
|||
return [self selectedText];
|
||||
// Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
|
||||
// object's AXSelectedText attribute. See bug 674612.
|
||||
// Also if there is no selected text, we return the full text.See bug 369710
|
||||
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
|
||||
return [self selectedText];
|
||||
return [self selectedText] ? : [self text];
|
||||
|
||||
// let mozAccessible handle all other attributes
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
|
@ -158,6 +162,20 @@ using namespace mozilla::a11y;
|
|||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (NSString*)text
|
||||
{
|
||||
if (!mGeckoTextAccessible)
|
||||
return nil;
|
||||
|
||||
nsAutoString text;
|
||||
nsresult rv =
|
||||
mGeckoTextAccessible->GetText(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT,
|
||||
text);
|
||||
NS_ENSURE_SUCCESS(rv, nil);
|
||||
|
||||
return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text);
|
||||
}
|
||||
|
||||
- (long)textLength
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
#import "mozAccessible.h"
|
||||
#import "mozActionElements.h"
|
||||
#import "mozHTMLAccessible.h"
|
||||
#import "mozTextAccessible.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
@ -108,10 +109,12 @@ nsAccessibleWrap::GetNativeType ()
|
|||
|
||||
case roles::AUTOCOMPLETE:
|
||||
return [mozComboboxAccessible class];
|
||||
|
||||
|
||||
case roles::HEADING:
|
||||
return [mozHeadingAccessible class];
|
||||
|
||||
case roles::ENTRY:
|
||||
case roles::STATICTEXT:
|
||||
case roles::HEADING:
|
||||
case roles::LABEL:
|
||||
case roles::CAPTION:
|
||||
case roles::ACCEL_LABEL:
|
||||
|
|
|
@ -147,7 +147,7 @@ static const NSString* AXRoles [] = {
|
|||
NSAccessibilityTextFieldRole, // ROLE_ENTRY
|
||||
NSAccessibilityStaticTextRole, // ROLE_CAPTION
|
||||
@"AXWebArea", // ROLE_DOCUMENT_FRAME
|
||||
NSAccessibilityStaticTextRole, // ROLE_HEADING
|
||||
@"AXHeading", // ROLE_HEADING
|
||||
NSAccessibilityGroupRole, // ROLE_PAGE
|
||||
NSAccessibilityGroupRole, // ROLE_SECTION
|
||||
NSAccessibilityUnknownRole, // ROLE_REDUNDANT_OBJECT
|
||||
|
|
|
@ -176,7 +176,8 @@
|
|||
<menuseparator class="appmenu-menuseparator"/>
|
||||
<menu id="appmenu_webDeveloper"
|
||||
label="&appMenuWebDeveloper.label;">
|
||||
<menupopup id="appmenu_webDeveloper_popup">
|
||||
<menupopup id="appmenu_webDeveloper_popup"
|
||||
onpopupshowing="onWebDeveloperMenuShowing();">
|
||||
<menuitem id="appmenu_webConsole"
|
||||
label="&webConsoleCmd.label;"
|
||||
type="checkbox"
|
||||
|
|
|
@ -531,7 +531,8 @@
|
|||
<menu id="webDeveloperMenu"
|
||||
label="&webDeveloperMenu.label;"
|
||||
accesskey="&webDeveloperMenu.accesskey;">
|
||||
<menupopup id="menuWebDeveloperPopup">
|
||||
<menupopup id="menuWebDeveloperPopup"
|
||||
onpopupshowing="onWebDeveloperMenuShowing();">
|
||||
<menuitem id="webConsole"
|
||||
type="checkbox"
|
||||
label="&webConsoleCmd.label;"
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
#ifdef 0
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Keeps thumbnails of open web pages up-to-date.
|
||||
*/
|
||||
let gBrowserThumbnails = {
|
||||
_captureDelayMS: 2000,
|
||||
|
||||
/**
|
||||
* Map of capture() timeouts assigned to their browsers.
|
||||
*/
|
||||
_timeouts: null,
|
||||
|
||||
/**
|
||||
* Cache for the PageThumbs module.
|
||||
*/
|
||||
_pageThumbs: null,
|
||||
|
||||
/**
|
||||
* List of tab events we want to listen for.
|
||||
*/
|
||||
_tabEvents: ["TabClose", "TabSelect"],
|
||||
|
||||
init: function Thumbnails_init() {
|
||||
gBrowser.addTabsProgressListener(this);
|
||||
|
||||
this._tabEvents.forEach(function (aEvent) {
|
||||
gBrowser.tabContainer.addEventListener(aEvent, this, false);
|
||||
}, this);
|
||||
|
||||
this._timeouts = new WeakMap();
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "_pageThumbs",
|
||||
"resource:///modules/PageThumbs.jsm", "PageThumbs");
|
||||
},
|
||||
|
||||
uninit: function Thumbnails_uninit() {
|
||||
gBrowser.removeTabsProgressListener(this);
|
||||
|
||||
this._tabEvents.forEach(function (aEvent) {
|
||||
gBrowser.tabContainer.removeEventListener(aEvent, this, false);
|
||||
}, this);
|
||||
|
||||
this._timeouts = null;
|
||||
this._pageThumbs = null;
|
||||
},
|
||||
|
||||
handleEvent: function Thumbnails_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "scroll":
|
||||
let browser = aEvent.currentTarget;
|
||||
if (this._timeouts.has(browser))
|
||||
this._delayedCapture(browser);
|
||||
break;
|
||||
case "TabSelect":
|
||||
this._delayedCapture(aEvent.target.linkedBrowser);
|
||||
break;
|
||||
case "TabClose": {
|
||||
this._clearTimeout(aEvent.target.linkedBrowser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* State change progress listener for all tabs.
|
||||
*/
|
||||
onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
|
||||
aRequest, aStateFlags, aStatus) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
|
||||
this._delayedCapture(aBrowser);
|
||||
},
|
||||
|
||||
_capture: function Thumbnails_capture(aBrowser) {
|
||||
if (this._shouldCapture(aBrowser)) {
|
||||
let canvas = this._pageThumbs.capture(aBrowser.contentWindow);
|
||||
this._pageThumbs.store(aBrowser.currentURI.spec, canvas);
|
||||
}
|
||||
},
|
||||
|
||||
_delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
|
||||
if (this._timeouts.has(aBrowser))
|
||||
clearTimeout(this._timeouts.get(aBrowser));
|
||||
else
|
||||
aBrowser.addEventListener("scroll", this, true);
|
||||
|
||||
let timeout = setTimeout(function () {
|
||||
this._clearTimeout(aBrowser);
|
||||
this._capture(aBrowser);
|
||||
}.bind(this), this._captureDelayMS);
|
||||
|
||||
this._timeouts.set(aBrowser, timeout);
|
||||
},
|
||||
|
||||
_shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
|
||||
let doc = aBrowser.contentDocument;
|
||||
|
||||
// FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
|
||||
// that currently regresses Talos SVG tests.
|
||||
if (doc instanceof SVGDocument || doc instanceof XMLDocument)
|
||||
return false;
|
||||
|
||||
// There's no point in taking screenshot of loading pages.
|
||||
if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
|
||||
return false;
|
||||
|
||||
// Don't take screenshots of about: pages.
|
||||
if (aBrowser.currentURI.schemeIs("about"))
|
||||
return false;
|
||||
|
||||
let channel = aBrowser.docShell.currentDocumentChannel;
|
||||
|
||||
try {
|
||||
// If the channel is a nsIHttpChannel get its http status code.
|
||||
let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
// Continue only if we have a 2xx status code.
|
||||
return Math.floor(httpChannel.responseStatus / 100) == 2;
|
||||
} catch (e) {
|
||||
// Not a http channel, we just assume a success status code.
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_clearTimeout: function Thumbnails_clearTimeout(aBrowser) {
|
||||
if (this._timeouts.has(aBrowser)) {
|
||||
aBrowser.removeEventListener("scroll", this, false);
|
||||
clearTimeout(this._timeouts.get(aBrowser));
|
||||
this._timeouts.delete(aBrowser);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -196,6 +196,7 @@ let gInitialPages = [
|
|||
#include browser-places.js
|
||||
#include browser-tabPreviews.js
|
||||
#include browser-tabview.js
|
||||
#include browser-thumbnails.js
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include browser-syncui.js
|
||||
|
@ -1501,6 +1502,8 @@ function prepareForStartup() {
|
|||
}
|
||||
|
||||
function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
||||
Cu.import("resource:///modules/TelemetryTimestamps.jsm");
|
||||
TelemetryTimestamps.add("delayedStartupStarted");
|
||||
gDelayedStartupTimeoutId = null;
|
||||
|
||||
Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
|
||||
|
@ -1703,6 +1706,7 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
|||
gSyncUI.init();
|
||||
#endif
|
||||
|
||||
gBrowserThumbnails.init();
|
||||
TabView.init();
|
||||
|
||||
setUrlAndSearchBarWidthForConditionalForwardButton();
|
||||
|
@ -1765,6 +1769,7 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
|||
window.addEventListener("dragover", MousePosTracker, false);
|
||||
|
||||
Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
|
||||
TelemetryTimestamps.add("delayedStartupFinished");
|
||||
}
|
||||
|
||||
function BrowserShutdown() {
|
||||
|
@ -1824,6 +1829,7 @@ function BrowserShutdown() {
|
|||
gPrefService.removeObserver(allTabs.prefName, allTabs);
|
||||
ctrlTab.uninit();
|
||||
TabView.uninit();
|
||||
gBrowserThumbnails.uninit();
|
||||
|
||||
try {
|
||||
FullZoom.destroy();
|
||||
|
@ -3109,20 +3115,13 @@ function FillInHTMLTooltip(tipElement)
|
|||
|
||||
[titleText, XLinkTitleText, SVGTitleText].forEach(function (t) {
|
||||
if (t && /\S/.test(t)) {
|
||||
|
||||
// Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be
|
||||
// replaced with spaces, and LFs should be removed entirely.
|
||||
// XXX Bug 322270: We don't preserve the result of entities like ,
|
||||
// which should result in a line break in the tooltip, because we can't
|
||||
// distinguish that from a literal character in the source by this point.
|
||||
t = t.replace(/[\r\t]/g, ' ');
|
||||
t = t.replace(/\n/g, '');
|
||||
// Make CRLF and CR render one line break each.
|
||||
t = t.replace(/\r\n?/g, '\n');
|
||||
|
||||
tipNode.setAttribute("label", t);
|
||||
retVal = true;
|
||||
}
|
||||
});
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -4789,6 +4788,7 @@ var XULBrowserWindow = {
|
|||
},
|
||||
|
||||
hideChromeForLocation: function(aLocation) {
|
||||
aLocation = aLocation.toLowerCase();
|
||||
return this.inContentWhitelist.some(function(aSpec) {
|
||||
return aSpec == aLocation;
|
||||
});
|
||||
|
@ -5201,7 +5201,7 @@ nsBrowserAccess.prototype = {
|
|||
let win, needToFocusWin;
|
||||
|
||||
// try the current window. if we're in a popup, fall back on the most recent browser window
|
||||
if (!window.document.documentElement.getAttribute("chromehidden"))
|
||||
if (window.toolbar.visible)
|
||||
win = window;
|
||||
else {
|
||||
win = Cc["@mozilla.org/browser/browserglue;1"]
|
||||
|
@ -5973,12 +5973,12 @@ function MultiplexHandler(event)
|
|||
var name = node.getAttribute('name');
|
||||
|
||||
if (name == 'detectorGroup') {
|
||||
SetForcedDetector(true);
|
||||
BrowserCharsetReload();
|
||||
SelectDetector(event, false);
|
||||
} else if (name == 'charsetGroup') {
|
||||
var charset = node.getAttribute('id');
|
||||
charset = charset.substring('charset.'.length, charset.length)
|
||||
SetForcedCharset(charset);
|
||||
BrowserSetForcedCharacterSet(charset);
|
||||
} else if (name == 'charsetCustomize') {
|
||||
//do nothing - please remove this else statement, once the charset prefs moves to the pref window
|
||||
} else {
|
||||
|
@ -6009,30 +6009,17 @@ function SelectDetector(event, doReload)
|
|||
}
|
||||
}
|
||||
|
||||
function SetForcedDetector(doReload)
|
||||
{
|
||||
BrowserSetForcedDetector(doReload);
|
||||
}
|
||||
|
||||
function SetForcedCharset(charset)
|
||||
{
|
||||
BrowserSetForcedCharacterSet(charset);
|
||||
}
|
||||
|
||||
function BrowserSetForcedCharacterSet(aCharset)
|
||||
{
|
||||
var docCharset = gBrowser.docShell.QueryInterface(Ci.nsIDocCharset);
|
||||
docCharset.charset = aCharset;
|
||||
gBrowser.docShell.charset = aCharset;
|
||||
// Save the forced character-set
|
||||
PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, aCharset);
|
||||
BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
BrowserCharsetReload();
|
||||
}
|
||||
|
||||
function BrowserSetForcedDetector(doReload)
|
||||
function BrowserCharsetReload()
|
||||
{
|
||||
gBrowser.documentCharsetInfo.forcedDetector = true;
|
||||
if (doReload)
|
||||
BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
}
|
||||
|
||||
function charsetMenuGetElement(parent, id) {
|
||||
|
@ -8922,9 +8909,10 @@ var TabContextMenu = {
|
|||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () {
|
||||
Cu.import("resource:///modules/HUDService.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/HUDService.jsm", tempScope);
|
||||
try {
|
||||
return HUDService.consoleUI;
|
||||
return tempScope.HUDService.consoleUI;
|
||||
}
|
||||
catch (ex) {
|
||||
Components.utils.reportError(ex);
|
||||
|
@ -9076,6 +9064,11 @@ var StyleEditor = {
|
|||
}
|
||||
};
|
||||
|
||||
function onWebDeveloperMenuShowing() {
|
||||
document.getElementById("Tools:WebConsole").setAttribute("checked", HUDConsoleUI.getOpenHUD() != null);
|
||||
}
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
|
||||
#ifdef XP_WIN
|
||||
// Only show resizers on Windows 2000 and XP
|
||||
|
@ -9150,6 +9143,7 @@ var MousePosTracker = {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
function focusNextFrame(event) {
|
||||
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||
let dir = event.shiftKey ? fm.MOVEFOCUS_BACKWARDDOC : fm.MOVEFOCUS_FORWARDDOC;
|
||||
|
|
|
@ -2375,10 +2375,6 @@
|
|||
onget="return this.mCurrentBrowser.contentViewerFile;"
|
||||
readonly="true"/>
|
||||
|
||||
<property name="documentCharsetInfo"
|
||||
onget="return this.mCurrentBrowser.documentCharsetInfo;"
|
||||
readonly="true"/>
|
||||
|
||||
<property name="contentDocument"
|
||||
onget="return this.mCurrentBrowser.contentDocument;"
|
||||
readonly="true"/>
|
||||
|
@ -3811,49 +3807,34 @@
|
|||
<binding id="tabbrowser-alltabs-popup"
|
||||
extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
<implementation implements="nsIDOMEventListener">
|
||||
<method name="_menuItemOnCommand">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
gBrowser.selectedTab = aEvent.target.tab;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_tabOnAttrModified">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
var tab = aEvent.target;
|
||||
this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
|
||||
if (tab.mCorrespondingMenuitem)
|
||||
this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_tabOnTabClose">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
var menuItem = aEvent.target.mCorrespondingMenuitem;
|
||||
if (menuItem)
|
||||
this.removeChild(menuItem);
|
||||
var tab = aEvent.target;
|
||||
if (tab.mCorrespondingMenuitem)
|
||||
this.removeChild(tab.mCorrespondingMenuitem);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="handleEvent">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
if (!aEvent.isTrusted)
|
||||
return;
|
||||
|
||||
switch (aEvent.type) {
|
||||
case "command":
|
||||
this._menuItemOnCommand(aEvent);
|
||||
break;
|
||||
case "TabAttrModified":
|
||||
this._tabOnAttrModified(aEvent);
|
||||
break;
|
||||
case "TabClose":
|
||||
this._tabOnTabClose(aEvent);
|
||||
break;
|
||||
case "TabOpen":
|
||||
this._createTabMenuItem(aEvent.originalTarget);
|
||||
break;
|
||||
case "scroll":
|
||||
this._updateTabsVisibilityStatus();
|
||||
break;
|
||||
|
@ -3874,8 +3855,6 @@
|
|||
if (!curTab) // "Tab Groups" menuitem and its menuseparator
|
||||
continue;
|
||||
let curTabBO = curTab.boxObject;
|
||||
if (!curTabBO) // "Tabs From Other Computers" menuitem
|
||||
continue;
|
||||
if (curTabBO.screenX >= tabstripBO.screenX &&
|
||||
curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
|
||||
this.childNodes[i].setAttribute("tabIsVisible", "true");
|
||||
|
@ -3896,14 +3875,10 @@
|
|||
|
||||
this._setMenuitemAttributes(menuItem, aTab);
|
||||
|
||||
// Keep some attributes of the menuitem in sync with its
|
||||
// corresponding tab (e.g. the tab label)
|
||||
aTab.mCorrespondingMenuitem = menuItem;
|
||||
menuItem.tab = aTab;
|
||||
menuItem.addEventListener("command", this, false);
|
||||
|
||||
this.appendChild(menuItem);
|
||||
return menuItem;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -3938,18 +3913,17 @@
|
|||
<handlers>
|
||||
<handler event="popupshowing">
|
||||
<![CDATA[
|
||||
// set up the menu popup
|
||||
var tabcontainer = gBrowser.tabContainer;
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
|
||||
// Listen for changes in the tab bar.
|
||||
tabcontainer.addEventListener("TabOpen", this, false);
|
||||
tabcontainer.addEventListener("TabAttrModified", this, false);
|
||||
tabcontainer.addEventListener("TabClose", this, false);
|
||||
tabcontainer.mTabstrip.addEventListener("scroll", this, false);
|
||||
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
this._createTabMenuItem(tabs[i]);
|
||||
if (!tabs[i].pinned)
|
||||
this._createTabMenuItem(tabs[i]);
|
||||
}
|
||||
this._updateTabsVisibilityStatus();
|
||||
]]></handler>
|
||||
|
@ -3960,14 +3934,12 @@
|
|||
for (let i = this.childNodes.length - 1; i > 0; i--) {
|
||||
let menuItem = this.childNodes[i];
|
||||
if (menuItem.tab) {
|
||||
menuItem.removeEventListener("command", this, false);
|
||||
menuItem.tab.mCorrespondingMenuitem = null;
|
||||
this.removeChild(menuItem);
|
||||
}
|
||||
}
|
||||
var tabcontainer = gBrowser.tabContainer;
|
||||
tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
|
||||
tabcontainer.removeEventListener("TabOpen", this, false);
|
||||
tabcontainer.removeEventListener("TabAttrModified", this, false);
|
||||
tabcontainer.removeEventListener("TabClose", this, false);
|
||||
]]></handler>
|
||||
|
@ -3988,6 +3960,11 @@
|
|||
XULBrowserWindow.setOverLink("", null);
|
||||
]]></handler>
|
||||
|
||||
<handler event="command"><![CDATA[
|
||||
if (event.target.tab)
|
||||
gBrowser.selectedTab = event.target.tab;
|
||||
]]></handler>
|
||||
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
|
|
@ -11,16 +11,16 @@ function test () {
|
|||
is(tooltip.getAttribute("label"), "This is a non-root SVG element title");
|
||||
|
||||
ok(FillInHTMLTooltip(doc.getElementById("text1"), "should get title"));
|
||||
is(tooltip.getAttribute("label"), " This is a title ");
|
||||
is(tooltip.getAttribute("label"), "\n\n\n This is a title\n\n ");
|
||||
|
||||
ok(!FillInHTMLTooltip(doc.getElementById("text2"), "should not get title"));
|
||||
|
||||
ok(!FillInHTMLTooltip(doc.getElementById("text3"), "should not get title"));
|
||||
|
||||
ok(FillInHTMLTooltip(doc.getElementById("link1"), "should get title"));
|
||||
is(tooltip.getAttribute("label"), " This is a title ");
|
||||
is(tooltip.getAttribute("label"), "\n This is a title\n ");
|
||||
ok(FillInHTMLTooltip(doc.getElementById("text4"), "should get title"));
|
||||
is(tooltip.getAttribute("label"), " This is a title ");
|
||||
is(tooltip.getAttribute("label"), "\n This is a title\n ");
|
||||
|
||||
ok(!FillInHTMLTooltip(doc.getElementById("link2"), "should not get title"));
|
||||
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
const testURL1 = "http://mochi.test:8888/browser/browser/base/content/test/browser_clearplugindata.html";
|
||||
const testURL2 = "http://mochi.test:8888/browser/browser/base/content/test/browser_clearplugindata_noage.html";
|
||||
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js");
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
const pluginHostIface = Ci.nsIPluginHost;
|
||||
var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
|
|
|
@ -104,7 +104,6 @@ function end_test() {
|
|||
|
||||
function test_url(aURL, aCanHide, aNextTest) {
|
||||
is_chrome_visible();
|
||||
|
||||
info("Page load");
|
||||
load_page(aURL, aCanHide, function() {
|
||||
info("Switch away");
|
||||
|
@ -163,5 +162,30 @@ function run_http_test_2() {
|
|||
// Should not hide the chrome
|
||||
function run_chrome_about_test_2() {
|
||||
info("Chrome about: tests");
|
||||
test_url("about:addons", true, end_test);
|
||||
test_url("about:addons", true, run_http_test3);
|
||||
}
|
||||
|
||||
function run_http_test3() {
|
||||
info("HTTP tests");
|
||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_3);
|
||||
}
|
||||
|
||||
// Should not hide the chrome
|
||||
function run_chrome_about_test_3() {
|
||||
info("Chrome about: tests");
|
||||
test_url("about:Addons", true, function(){
|
||||
info("Tabs on top");
|
||||
TabsOnTop.enabled = true;
|
||||
run_http_test4();
|
||||
});
|
||||
}
|
||||
|
||||
function run_http_test4() {
|
||||
info("HTTP tests");
|
||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_4);
|
||||
}
|
||||
|
||||
function run_chrome_about_test_4() {
|
||||
info("Chrome about: tests");
|
||||
test_url("about:Addons", true, end_test);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// Bug 474792 - Clear "Never remember passwords for this site" when
|
||||
// clearing site-specific settings in Clear Recent History dialog
|
||||
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js");
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
function test() {
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// Bug 380852 - Delete permission manager entries in Clear Recent History
|
||||
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js");
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
function test() {
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@ const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManage
|
|||
const bhist = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIBrowserHistory);
|
||||
const formhist = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
|
||||
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js");
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
function test() {
|
||||
|
||||
|
|
|
@ -50,9 +50,10 @@
|
|||
* browser/base/content/test/browser_sanitize-timespans.js.
|
||||
*/
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader).
|
||||
loadSubScript("chrome://browser/content/sanitize.js");
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
|
|
|
@ -56,7 +56,7 @@ function getTopWin(skipPopups) {
|
|||
// whether it's the frontmost window, since commands can be executed in
|
||||
// background windows (bug 626148).
|
||||
if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
|
||||
(!skipPopups || !top.document.documentElement.getAttribute("chromehidden")))
|
||||
(!skipPopups || top.toolbar.visible))
|
||||
return top;
|
||||
|
||||
if (skipPopups) {
|
||||
|
@ -206,7 +206,7 @@ function openLinkIn(url, where, params) {
|
|||
|
||||
var w = getTopWin();
|
||||
if ((where == "tab" || where == "tabshifted") &&
|
||||
w && w.document.documentElement.getAttribute("chromehidden")) {
|
||||
w && !w.toolbar.visible) {
|
||||
w = getTopWin(true);
|
||||
aRelatedToCurrent = false;
|
||||
}
|
||||
|
|
Двоичные данные
browser/branding/aurora/dsstore
Двоичные данные
browser/branding/aurora/dsstore
Двоичный файл не отображается.
Двоичные данные
browser/branding/nightly/dsstore
Двоичные данные
browser/branding/nightly/dsstore
Двоичный файл не отображается.
|
@ -71,6 +71,7 @@ PARALLEL_DIRS = \
|
|||
shell \
|
||||
sidebar \
|
||||
tabview \
|
||||
thumbnails \
|
||||
migration \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -49,16 +49,15 @@ USE_STATIC_LIBS = 1
|
|||
endif
|
||||
|
||||
|
||||
CPPSRCS = nsBrowserProfileMigratorUtils.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH)_$(GNU_CXX),WINNT_)
|
||||
CPPSRCS += nsIEProfileMigrator.cpp \
|
||||
nsBrowserProfileMigratorUtils.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
CPPSRCS += nsSafariProfileMigrator.cpp \
|
||||
nsBrowserProfileMigratorUtils.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ BrowserGlue.prototype = {
|
|||
Services.obs.removeObserver(this, "browser-delayed-startup-finished");
|
||||
break;
|
||||
case "sessionstore-windows-restored":
|
||||
this._onBrowserStartup();
|
||||
this._onWindowsRestored();
|
||||
break;
|
||||
case "browser:purge-session-history":
|
||||
// reset the console service's error buffer
|
||||
|
@ -373,8 +373,8 @@ BrowserGlue.prototype = {
|
|||
this._sanitizer.onShutdown();
|
||||
},
|
||||
|
||||
// Browser startup complete. All initial windows have opened.
|
||||
_onBrowserStartup: function BG__onBrowserStartup() {
|
||||
// All initial windows have opened.
|
||||
_onWindowsRestored: function BG__onWindowsRestored() {
|
||||
// Show about:rights notification, if needed.
|
||||
if (this._shouldShowRights()) {
|
||||
this._showRightsNotification();
|
||||
|
@ -1442,7 +1442,7 @@ BrowserGlue.prototype = {
|
|||
getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() {
|
||||
function isFullBrowserWindow(win) {
|
||||
return !win.closed &&
|
||||
!win.document.documentElement.getAttribute("chromehidden");
|
||||
win.toolbar.visible;
|
||||
}
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
|
|
|
@ -130,6 +130,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
// debug.js adds NS_ASSERT. cf. bug 669196
|
||||
Cu.import("resource://gre/modules/debug.js");
|
||||
|
||||
Cu.import("resource:///modules/TelemetryTimestamps.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
return NetUtil;
|
||||
|
@ -294,6 +296,7 @@ SessionStoreService.prototype = {
|
|||
* Initialize the component
|
||||
*/
|
||||
initService: function() {
|
||||
TelemetryTimestamps.add("sessionRestoreInitialized");
|
||||
OBSERVING.forEach(function(aTopic) {
|
||||
Services.obs.addObserver(this, aTopic, true);
|
||||
}, this);
|
||||
|
@ -833,7 +836,7 @@ SessionStoreService.prototype = {
|
|||
this._windows[aWindow.__SSi]._restoring = true;
|
||||
if (!aWindow.toolbar.visible)
|
||||
this._windows[aWindow.__SSi].isPopup = true;
|
||||
|
||||
|
||||
// perform additional initialization when the first window is loading
|
||||
if (this._loadState == STATE_STOPPED) {
|
||||
this._loadState = STATE_RUNNING;
|
||||
|
@ -841,6 +844,7 @@ SessionStoreService.prototype = {
|
|||
|
||||
// restore a crashed session resp. resume the last session if requested
|
||||
if (this._initialState) {
|
||||
TelemetryTimestamps.add("sessionRestoreRestoring");
|
||||
// make sure that the restored tabs are first in the window
|
||||
this._initialState._firstTabs = true;
|
||||
this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
|
||||
|
|
|
@ -48,7 +48,7 @@ function test() {
|
|||
var file = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties)
|
||||
.get("TmpD", Components.interfaces.nsIFile);
|
||||
filePath = file.path;
|
||||
var filePath = file.path;
|
||||
|
||||
let fieldList = {
|
||||
"//input[@name='input']": Date.now().toString(),
|
||||
|
|
|
@ -42,13 +42,13 @@ function test() {
|
|||
.get("TmpD", Components.interfaces.nsILocalFile);
|
||||
file.append("346337_test1.file");
|
||||
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
filePath1 = file.path;
|
||||
var filePath1 = file.path;
|
||||
file = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties)
|
||||
.get("TmpD", Components.interfaces.nsILocalFile);
|
||||
file.append("346337_test2.file");
|
||||
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
filePath2 = file.path;
|
||||
var filePath2 = file.path;
|
||||
|
||||
let fieldList = {
|
||||
"//input[@name='input']": Date.now().toString(),
|
||||
|
|
|
@ -741,6 +741,11 @@ iQClass.prototype = {
|
|||
if (pair.original == func) {
|
||||
handler = pair.modified;
|
||||
elem.iQEventData[type].splice(a, 1);
|
||||
if (!elem.iQEventData[type].length) {
|
||||
delete elem.iQEventData[type];
|
||||
if (!Object.keys(elem.iQEventData).length)
|
||||
delete elem.iQEventData;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -765,10 +770,10 @@ iQClass.prototype = {
|
|||
if (!elem.iQEventData)
|
||||
continue;
|
||||
|
||||
for (let type in elem.iQEventData) {
|
||||
while (elem.iQEventData[type].length)
|
||||
Object.keys(elem.iQEventData).forEach(function (type) {
|
||||
while (elem.iQEventData && elem.iQEventData[type])
|
||||
this.unbind(type, elem.iQEventData[type][0].original);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
component {5a4ae9b5-f475-48ae-9dce-0b4c1d347884} PageThumbsProtocol.js
|
||||
contract @mozilla.org/network/protocol;1?name=moz-page-thumb {5a4ae9b5-f475-48ae-9dce-0b4c1d347884}
|
|
@ -0,0 +1,28 @@
|
|||
# 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/.
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
BrowserPageThumbs.manifest \
|
||||
PageThumbsProtocol.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_JS_MODULES = \
|
||||
PageThumbs.jsm \
|
||||
$(NULL)
|
||||
|
||||
# FIXME Bug 721422 - Re-enable tests and make them work with URI_DANGEROUS_TO_LOAD
|
||||
#ifdef ENABLE_TESTS
|
||||
# DIRS += test
|
||||
#endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
XPIDL_FLAGS += -I$(topsrcdir)/browser/components/
|
|
@ -0,0 +1,259 @@
|
|||
/* 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";
|
||||
|
||||
let EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsCache"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/**
|
||||
* The default width for page thumbnails.
|
||||
*
|
||||
* Hint: This is the default value because the 'New Tab Page' is the only
|
||||
* client for now.
|
||||
*/
|
||||
const THUMBNAIL_WIDTH = 201;
|
||||
|
||||
/**
|
||||
* The default height for page thumbnails.
|
||||
*
|
||||
* Hint: This is the default value because the 'New Tab Page' is the only
|
||||
* client for now.
|
||||
*/
|
||||
const THUMBNAIL_HEIGHT = 127;
|
||||
|
||||
/**
|
||||
* The default background color for page thumbnails.
|
||||
*/
|
||||
const THUMBNAIL_BG_COLOR = "#fff";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Singleton providing functionality for capturing web page thumbnails and for
|
||||
* accessing them if already cached.
|
||||
*/
|
||||
let PageThumbs = {
|
||||
/**
|
||||
* The scheme to use for thumbnail urls.
|
||||
*/
|
||||
get scheme() "moz-page-thumb",
|
||||
|
||||
/**
|
||||
* The static host to use for thumbnail urls.
|
||||
*/
|
||||
get staticHost() "thumbnail",
|
||||
|
||||
/**
|
||||
* The thumbnails' image type.
|
||||
*/
|
||||
get contentType() "image/png",
|
||||
|
||||
/**
|
||||
* Gets the thumbnail image's url for a given web page's url.
|
||||
* @param aUrl The web page's url that is depicted in the thumbnail.
|
||||
* @return The thumbnail image's url.
|
||||
*/
|
||||
getThumbnailURL: function PageThumbs_getThumbnailURL(aUrl) {
|
||||
return this.scheme + "://" + this.staticHost +
|
||||
"?url=" + encodeURIComponent(aUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a canvas containing a thumbnail depicting the given window.
|
||||
* @param aWindow The DOM window to capture a thumbnail from.
|
||||
* @return The newly created canvas containing the image data.
|
||||
*/
|
||||
capture: function PageThumbs_capture(aWindow) {
|
||||
let [sw, sh, scale] = this._determineCropSize(aWindow);
|
||||
|
||||
let canvas = this._createCanvas();
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
// Scale the canvas accordingly.
|
||||
ctx.scale(scale, scale);
|
||||
|
||||
try {
|
||||
// Draw the window contents to the canvas.
|
||||
ctx.drawWindow(aWindow, 0, 0, sw, sh, THUMBNAIL_BG_COLOR,
|
||||
ctx.DRAWWINDOW_DO_NOT_FLUSH);
|
||||
} catch (e) {
|
||||
// We couldn't draw to the canvas for some reason.
|
||||
}
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores the image data contained in the given canvas to the underlying
|
||||
* storage.
|
||||
* @param aKey The key to use for the storage.
|
||||
* @param aCanvas The canvas containing the thumbnail's image data.
|
||||
* @param aCallback The function to be called when the canvas data has been
|
||||
* stored (optional).
|
||||
*/
|
||||
store: function PageThumbs_store(aKey, aCanvas, aCallback) {
|
||||
let self = this;
|
||||
|
||||
function finish(aSuccessful) {
|
||||
if (aCallback)
|
||||
aCallback(aSuccessful);
|
||||
}
|
||||
|
||||
// Get a writeable cache entry.
|
||||
PageThumbsCache.getWriteEntry(aKey, function (aEntry) {
|
||||
if (!aEntry) {
|
||||
finish(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract image data from the canvas.
|
||||
self._readImageData(aCanvas, function (aData) {
|
||||
let outputStream = aEntry.openOutputStream(0);
|
||||
|
||||
// Write the image data to the cache entry.
|
||||
NetUtil.asyncCopy(aData, outputStream, function (aResult) {
|
||||
let success = Components.isSuccessCode(aResult);
|
||||
if (success)
|
||||
aEntry.markValid();
|
||||
|
||||
aEntry.close();
|
||||
finish(success);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads the image data from a given canvas and passes it to the callback.
|
||||
* @param aCanvas The canvas to read the image data from.
|
||||
* @param aCallback The function that the image data is passed to.
|
||||
*/
|
||||
_readImageData: function PageThumbs_readImageData(aCanvas, aCallback) {
|
||||
let dataUri = aCanvas.toDataURL(PageThumbs.contentType, "");
|
||||
let uri = Services.io.newURI(dataUri, "UTF8", null);
|
||||
|
||||
NetUtil.asyncFetch(uri, function (aData, aResult) {
|
||||
if (Components.isSuccessCode(aResult) && aData && aData.available())
|
||||
aCallback(aData);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the crop size for a given content window.
|
||||
* @param aWindow The content window.
|
||||
* @return An array containing width, height and scale.
|
||||
*/
|
||||
_determineCropSize: function PageThumbs_determineCropSize(aWindow) {
|
||||
let sw = aWindow.innerWidth;
|
||||
let sh = aWindow.innerHeight;
|
||||
|
||||
let scale = Math.max(THUMBNAIL_WIDTH / sw, THUMBNAIL_HEIGHT / sh);
|
||||
let scaledWidth = sw * scale;
|
||||
let scaledHeight = sh * scale;
|
||||
|
||||
if (scaledHeight > THUMBNAIL_HEIGHT)
|
||||
sh -= Math.floor(Math.abs(scaledHeight - THUMBNAIL_HEIGHT) * scale);
|
||||
|
||||
if (scaledWidth > THUMBNAIL_WIDTH)
|
||||
sw -= Math.floor(Math.abs(scaledWidth - THUMBNAIL_WIDTH) * scale);
|
||||
|
||||
return [sw, sh, scale];
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new hidden canvas element.
|
||||
* @return The newly created canvas.
|
||||
*/
|
||||
_createCanvas: function PageThumbs_createCanvas() {
|
||||
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||
let canvas = doc.createElementNS(HTML_NAMESPACE, "canvas");
|
||||
canvas.mozOpaque = true;
|
||||
canvas.mozImageSmoothingEnabled = true;
|
||||
canvas.width = THUMBNAIL_WIDTH;
|
||||
canvas.height = THUMBNAIL_HEIGHT;
|
||||
return canvas;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A singleton handling the storage of page thumbnails.
|
||||
*/
|
||||
let PageThumbsCache = {
|
||||
/**
|
||||
* Calls the given callback with a cache entry opened for reading.
|
||||
* @param aKey The key identifying the desired cache entry.
|
||||
* @param aCallback The callback that is called when the cache entry is ready.
|
||||
*/
|
||||
getReadEntry: function Cache_getReadEntry(aKey, aCallback) {
|
||||
// Try to open the desired cache entry.
|
||||
this._openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls the given callback with a cache entry opened for writing.
|
||||
* @param aKey The key identifying the desired cache entry.
|
||||
* @param aCallback The callback that is called when the cache entry is ready.
|
||||
*/
|
||||
getWriteEntry: function Cache_getWriteEntry(aKey, aCallback) {
|
||||
// Try to open the desired cache entry.
|
||||
this._openCacheEntry(aKey, Ci.nsICache.ACCESS_WRITE, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the cache entry identified by the given key.
|
||||
* @param aKey The key identifying the desired cache entry.
|
||||
* @param aAccess The desired access mode (see nsICache.ACCESS_* constants).
|
||||
* @param aCallback The function to be called when the cache entry was opened.
|
||||
*/
|
||||
_openCacheEntry: function Cache_openCacheEntry(aKey, aAccess, aCallback) {
|
||||
function onCacheEntryAvailable(aEntry, aAccessGranted, aStatus) {
|
||||
let validAccess = aAccess == aAccessGranted;
|
||||
let validStatus = Components.isSuccessCode(aStatus);
|
||||
|
||||
// Check if a valid entry was passed and if the
|
||||
// access we requested was actually granted.
|
||||
if (aEntry && !(validAccess && validStatus)) {
|
||||
aEntry.close();
|
||||
aEntry = null;
|
||||
}
|
||||
|
||||
aCallback(aEntry);
|
||||
}
|
||||
|
||||
let listener = this._createCacheListener(onCacheEntryAvailable);
|
||||
this._cacheSession.asyncOpenCacheEntry(aKey, aAccess, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a cache listener implementing the nsICacheListener interface.
|
||||
* @param aCallback The callback to be called when the cache entry is available.
|
||||
* @return The new cache listener.
|
||||
*/
|
||||
_createCacheListener: function Cache_createCacheListener(aCallback) {
|
||||
return {
|
||||
onCacheEntryAvailable: aCallback,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener])
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a lazy getter for the cache session.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(PageThumbsCache, "_cacheSession", function () {
|
||||
return Services.cache.createSession(PageThumbs.scheme,
|
||||
Ci.nsICache.STORE_ON_DISK, true);
|
||||
});
|
|
@ -0,0 +1,448 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* PageThumbsProtocol.js
|
||||
*
|
||||
* This file implements the moz-page-thumb:// protocol and the corresponding
|
||||
* channel delivering cached thumbnails.
|
||||
*
|
||||
* URL structure:
|
||||
*
|
||||
* moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F
|
||||
*
|
||||
* This URL requests an image for 'http://www.mozilla.org/'.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource:///modules/PageThumbs.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Implements the thumbnail protocol handler responsible for moz-page-thumb: URIs.
|
||||
*/
|
||||
function Protocol() {
|
||||
}
|
||||
|
||||
Protocol.prototype = {
|
||||
/**
|
||||
* The scheme used by this protocol.
|
||||
*/
|
||||
get scheme() PageThumbs.scheme,
|
||||
|
||||
/**
|
||||
* The default port for this protocol (we don't support ports).
|
||||
*/
|
||||
get defaultPort() -1,
|
||||
|
||||
/**
|
||||
* The flags specific to this protocol implementation.
|
||||
*/
|
||||
get protocolFlags() {
|
||||
return Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
|
||||
Ci.nsIProtocolHandler.URI_NORELATIVE |
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new URI object that is suitable for loading by this protocol.
|
||||
* @param aSpec The URI string in UTF8 encoding.
|
||||
* @param aOriginCharset The charset of the document from which the URI originated.
|
||||
* @return The newly created URI.
|
||||
*/
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
uri.spec = aSpec;
|
||||
return uri;
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructs a new channel from the given URI for this protocol handler.
|
||||
* @param aURI The URI for which to construct a channel.
|
||||
* @return The newly created channel.
|
||||
*/
|
||||
newChannel: function Proto_newChannel(aURI) {
|
||||
return new Channel(aURI);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decides whether to allow a blacklisted port.
|
||||
* @return Always false, we'll never allow ports.
|
||||
*/
|
||||
allowPort: function () false,
|
||||
|
||||
classID: Components.ID("{5a4ae9b5-f475-48ae-9dce-0b4c1d347884}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
|
||||
};
|
||||
|
||||
let NSGetFactory = XPCOMUtils.generateNSGetFactory([Protocol]);
|
||||
|
||||
/**
|
||||
* A channel implementation responsible for delivering cached thumbnails.
|
||||
*/
|
||||
function Channel(aURI) {
|
||||
this._uri = aURI;
|
||||
|
||||
// nsIChannel
|
||||
this.originalURI = aURI;
|
||||
|
||||
// nsIHttpChannel
|
||||
this._responseHeaders = {"content-type": PageThumbs.contentType};
|
||||
}
|
||||
|
||||
Channel.prototype = {
|
||||
/**
|
||||
* Tracks if the channel has been opened, yet.
|
||||
*/
|
||||
_wasOpened: false,
|
||||
|
||||
/**
|
||||
* Opens this channel asynchronously.
|
||||
* @param aListener The listener that receives the channel data when available.
|
||||
* @param aContext A custom context passed to the listener's methods.
|
||||
*/
|
||||
asyncOpen: function Channel_asyncOpen(aListener, aContext) {
|
||||
if (this._wasOpened)
|
||||
throw Cr.NS_ERROR_ALREADY_OPENED;
|
||||
|
||||
if (this.canceled)
|
||||
return;
|
||||
|
||||
this._listener = aListener;
|
||||
this._context = aContext;
|
||||
|
||||
this._isPending = true;
|
||||
this._wasOpened = true;
|
||||
|
||||
// Try to read the data from the thumbnail cache.
|
||||
this._readCache(function (aData) {
|
||||
// Update response if there's no data.
|
||||
if (!aData) {
|
||||
this._responseStatus = 404;
|
||||
this._responseText = "Not Found";
|
||||
}
|
||||
|
||||
this._startRequest();
|
||||
|
||||
if (!this.canceled) {
|
||||
this._addToLoadGroup();
|
||||
|
||||
if (aData)
|
||||
this._serveData(aData);
|
||||
|
||||
if (!this.canceled)
|
||||
this._stopRequest();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads a data stream from the cache entry.
|
||||
* @param aCallback The callback the data is passed to.
|
||||
*/
|
||||
_readCache: function Channel_readCache(aCallback) {
|
||||
let {url} = parseURI(this._uri);
|
||||
|
||||
// Return early if there's no valid URL given.
|
||||
if (!url) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get a cache entry.
|
||||
PageThumbsCache.getReadEntry(url, function (aEntry) {
|
||||
let inputStream = aEntry && aEntry.openInputStream(0);
|
||||
|
||||
function closeEntryAndFinish(aData) {
|
||||
if (aEntry) {
|
||||
aEntry.close();
|
||||
}
|
||||
aCallback(aData);
|
||||
}
|
||||
|
||||
// Check if we have a valid entry and if it has any data.
|
||||
if (!inputStream || !inputStream.available()) {
|
||||
closeEntryAndFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read the cache entry's data.
|
||||
NetUtil.asyncFetch(inputStream, function (aData, aStatus) {
|
||||
// We might have been canceled while waiting.
|
||||
if (this.canceled)
|
||||
return;
|
||||
|
||||
// Check if we have a valid data stream.
|
||||
if (!Components.isSuccessCode(aStatus) || !aData.available())
|
||||
aData = null;
|
||||
|
||||
closeEntryAndFinish(aData);
|
||||
}.bind(this));
|
||||
} catch (e) {
|
||||
closeEntryAndFinish();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls onStartRequest on the channel listener.
|
||||
*/
|
||||
_startRequest: function Channel_startRequest() {
|
||||
try {
|
||||
this._listener.onStartRequest(this, this._context);
|
||||
} catch (e) {
|
||||
// The listener might throw if the request has been canceled.
|
||||
this.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls onDataAvailable on the channel listener and passes the data stream.
|
||||
* @param aData The data to be delivered.
|
||||
*/
|
||||
_serveData: function Channel_serveData(aData) {
|
||||
try {
|
||||
let available = aData.available();
|
||||
this._listener.onDataAvailable(this, this._context, aData, 0, available);
|
||||
} catch (e) {
|
||||
// The listener might throw if the request has been canceled.
|
||||
this.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls onStopRequest on the channel listener.
|
||||
*/
|
||||
_stopRequest: function Channel_stopRequest() {
|
||||
try {
|
||||
this._listener.onStopRequest(this, this._context, this.status);
|
||||
} catch (e) {
|
||||
// This might throw but is generally ignored.
|
||||
}
|
||||
|
||||
// The request has finished, clean up after ourselves.
|
||||
this._cleanup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds this request to the load group, if any.
|
||||
*/
|
||||
_addToLoadGroup: function Channel_addToLoadGroup() {
|
||||
if (this.loadGroup)
|
||||
this.loadGroup.addRequest(this, this._context);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes this request from its load group, if any.
|
||||
*/
|
||||
_removeFromLoadGroup: function Channel_removeFromLoadGroup() {
|
||||
if (!this.loadGroup)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.loadGroup.removeRequest(this, this._context, this.status);
|
||||
} catch (e) {
|
||||
// This might throw but is ignored.
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleans up the channel when the request has finished.
|
||||
*/
|
||||
_cleanup: function Channel_cleanup() {
|
||||
this._removeFromLoadGroup();
|
||||
this.loadGroup = null;
|
||||
|
||||
this._isPending = false;
|
||||
|
||||
delete this._listener;
|
||||
delete this._context;
|
||||
},
|
||||
|
||||
/* :::::::: nsIChannel ::::::::::::::: */
|
||||
|
||||
contentType: PageThumbs.contentType,
|
||||
contentLength: -1,
|
||||
owner: null,
|
||||
contentCharset: null,
|
||||
notificationCallbacks: null,
|
||||
|
||||
get URI() this._uri,
|
||||
get securityInfo() null,
|
||||
|
||||
/**
|
||||
* Opens this channel synchronously. Not supported.
|
||||
*/
|
||||
open: function Channel_open() {
|
||||
// Synchronous data delivery is not implemented.
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/* :::::::: nsIHttpChannel ::::::::::::::: */
|
||||
|
||||
redirectionLimit: 10,
|
||||
requestMethod: "GET",
|
||||
allowPipelining: true,
|
||||
referrer: null,
|
||||
|
||||
get requestSucceeded() true,
|
||||
|
||||
_responseStatus: 200,
|
||||
get responseStatus() this._responseStatus,
|
||||
|
||||
_responseText: "OK",
|
||||
get responseStatusText() this._responseText,
|
||||
|
||||
/**
|
||||
* Checks if the server sent the equivalent of a "Cache-control: no-cache"
|
||||
* response header.
|
||||
* @return Always false.
|
||||
*/
|
||||
isNoCacheResponse: function () false,
|
||||
|
||||
/**
|
||||
* Checks if the server sent the equivalent of a "Cache-control: no-cache"
|
||||
* response header.
|
||||
* @return Always false.
|
||||
*/
|
||||
isNoStoreResponse: function () false,
|
||||
|
||||
/**
|
||||
* Returns the value of a particular request header. Not implemented.
|
||||
*/
|
||||
getRequestHeader: function Channel_getRequestHeader() {
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called to set the value of a particular request header.
|
||||
* Not implemented.
|
||||
*/
|
||||
setRequestHeader: function Channel_setRequestHeader() {
|
||||
if (this._wasOpened)
|
||||
throw Cr.NS_ERROR_IN_PROGRESS;
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this method to visit all request headers. Not implemented.
|
||||
*/
|
||||
visitRequestHeaders: function () {},
|
||||
|
||||
/**
|
||||
* Gets the value of a particular response header.
|
||||
* @param aHeader The case-insensitive name of the response header to query.
|
||||
* @return The header value.
|
||||
*/
|
||||
getResponseHeader: function Channel_getResponseHeader(aHeader) {
|
||||
let name = aHeader.toLowerCase();
|
||||
if (name in this._responseHeaders)
|
||||
return this._responseHeaders[name];
|
||||
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called to set the value of a particular response header.
|
||||
* @param aHeader The case-insensitive name of the response header to query.
|
||||
* @param aValue The response header value to set.
|
||||
*/
|
||||
setResponseHeader: function Channel_setResponseHeader(aHeader, aValue, aMerge) {
|
||||
let name = aHeader.toLowerCase();
|
||||
if (!aValue && !aMerge)
|
||||
delete this._responseHeaders[name];
|
||||
else
|
||||
this._responseHeaders[name] = aValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this method to visit all response headers.
|
||||
* @param aVisitor The header visitor.
|
||||
*/
|
||||
visitResponseHeaders: function Channel_visitResponseHeaders(aVisitor) {
|
||||
for (let name in this._responseHeaders) {
|
||||
let value = this._responseHeaders[name];
|
||||
|
||||
try {
|
||||
aVisitor.visitHeader(name, value);
|
||||
} catch (e) {
|
||||
// The visitor can throw to stop the iteration.
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* :::::::: nsIRequest ::::::::::::::: */
|
||||
|
||||
loadFlags: Ci.nsIRequest.LOAD_NORMAL,
|
||||
loadGroup: null,
|
||||
|
||||
get name() this._uri.spec,
|
||||
|
||||
_status: Cr.NS_OK,
|
||||
get status() this._status,
|
||||
|
||||
_isPending: false,
|
||||
isPending: function () this._isPending,
|
||||
|
||||
resume: function () {},
|
||||
suspend: function () {},
|
||||
|
||||
/**
|
||||
* Cancels this request.
|
||||
* @param aStatus The reason for cancelling.
|
||||
*/
|
||||
cancel: function Channel_cancel(aStatus) {
|
||||
if (this.canceled)
|
||||
return;
|
||||
|
||||
this._isCanceled = true;
|
||||
this._status = aStatus;
|
||||
|
||||
this._cleanup();
|
||||
},
|
||||
|
||||
/* :::::::: nsIHttpChannelInternal ::::::::::::::: */
|
||||
|
||||
documentURI: null,
|
||||
|
||||
_isCanceled: false,
|
||||
get canceled() this._isCanceled,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel,
|
||||
Ci.nsIHttpChannel,
|
||||
Ci.nsIHttpChannelInternal,
|
||||
Ci.nsIRequest])
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a given URI and extracts all parameters relevant to this protocol.
|
||||
* @param aURI The URI to parse.
|
||||
* @return The parsed parameters.
|
||||
*/
|
||||
function parseURI(aURI) {
|
||||
let {scheme, staticHost} = PageThumbs;
|
||||
let re = new RegExp("^" + scheme + "://" + staticHost + ".*?\\?");
|
||||
let query = aURI.spec.replace(re, "");
|
||||
let params = {};
|
||||
|
||||
query.split("&").forEach(function (aParam) {
|
||||
let [key, value] = aParam.split("=").map(decodeURIComponent);
|
||||
params[key.toLowerCase()] = value;
|
||||
});
|
||||
|
||||
return params;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# 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/.
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = browser/components/thumbnails/test
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_thumbnails_cache.js \
|
||||
browser_thumbnails_capture.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
|
@ -0,0 +1,34 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests ensure that saving a thumbnail to the cache works. They also
|
||||
* retrieve the thumbnail and display it using an <img> element to compare
|
||||
* its pixel colors.
|
||||
*/
|
||||
function runTests() {
|
||||
// Create a new tab with a red background.
|
||||
yield addTab("data:text/html,<body bgcolor=ff0000></body>");
|
||||
let cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
|
||||
// Capture a thumbnail for the tab.
|
||||
let canvas = PageThumbs.capture(cw);
|
||||
|
||||
// Store the tab into the thumbnail cache.
|
||||
yield PageThumbs.store("key", canvas, next);
|
||||
|
||||
let {width, height} = canvas;
|
||||
let thumb = PageThumbs.getThumbnailURL("key", width, height);
|
||||
|
||||
// Create a new tab with an image displaying the previously stored thumbnail.
|
||||
yield addTab("data:text/html,<img src='" + thumb + "'/>" +
|
||||
"<canvas width=" + width + " height=" + height + "/>");
|
||||
|
||||
cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
let [img, canvas] = cw.document.querySelectorAll("img, canvas");
|
||||
|
||||
// Draw the image to a canvas and compare the pixel color values.
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
checkCanvasColor(ctx, 255, 0, 0, "we have a red image and canvas");
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests ensure that capturing a site's screenshot to a canvas actually
|
||||
* works.
|
||||
*/
|
||||
function runTests() {
|
||||
// Create a tab with a red background.
|
||||
yield addTab("data:text/html,<body bgcolor=ff0000></body>");
|
||||
checkCurrentThumbnailColor(255, 0, 0, "we have a red thumbnail");
|
||||
|
||||
// Load a page with a green background.
|
||||
yield navigateTo("data:text/html,<body bgcolor=00ff00></body>");
|
||||
checkCurrentThumbnailColor(0, 255, 0, "we have a green thumbnail");
|
||||
|
||||
// Load a page with a blue background.
|
||||
yield navigateTo("data:text/html,<body bgcolor=0000ff></body>");
|
||||
checkCurrentThumbnailColor(0, 0, 255, "we have a blue thumbnail");
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a thumbnail of the currently selected tab and checks the color of
|
||||
* the resulting canvas.
|
||||
* @param aRed The red component's intensity.
|
||||
* @param aGreen The green component's intensity.
|
||||
* @param aBlue The blue component's intensity.
|
||||
* @param aMessage The info message to print when checking the pixel color.
|
||||
*/
|
||||
function checkCurrentThumbnailColor(aRed, aGreen, aBlue, aMessage) {
|
||||
let tab = gBrowser.selectedTab;
|
||||
let cw = tab.linkedBrowser.contentWindow;
|
||||
|
||||
let canvas = PageThumbs.capture(cw);
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/PageThumbs.jsm");
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Provide the default test function to start our test runner.
|
||||
*/
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* The test runner that controls the execution flow of our tests.
|
||||
*/
|
||||
let TestRunner = {
|
||||
/**
|
||||
* Starts the test runner.
|
||||
*/
|
||||
run: function () {
|
||||
waitForExplicitFinish();
|
||||
|
||||
this._iter = runTests();
|
||||
this.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs the next available test or finishes if there's no test left.
|
||||
*/
|
||||
next: function () {
|
||||
try {
|
||||
TestRunner._iter.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Continues the current test execution.
|
||||
*/
|
||||
function next() {
|
||||
TestRunner.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tab with the given URI.
|
||||
* @param aURI The URI that's loaded in the tab.
|
||||
*/
|
||||
function addTab(aURI) {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
|
||||
whenBrowserLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a new URI into the currently selected tab.
|
||||
* @param aURI The URI to load.
|
||||
*/
|
||||
function navigateTo(aURI) {
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
whenBrowserLoaded(browser);
|
||||
browser.loadURI(aURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues the current test execution when a load event for the given browser
|
||||
* has been received
|
||||
* @param aBrowser The browser to listen on.
|
||||
*/
|
||||
function whenBrowserLoaded(aBrowser) {
|
||||
aBrowser.addEventListener("load", function onLoad() {
|
||||
aBrowser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(next);
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the top-left pixel of a given canvas' 2d context for a given color.
|
||||
* @param aContext The 2D context of a canvas.
|
||||
* @param aRed The red component's intensity.
|
||||
* @param aGreen The green component's intensity.
|
||||
* @param aBlue The blue component's intensity.
|
||||
* @param aMessage The info message to print when comparing the pixel color.
|
||||
*/
|
||||
function checkCanvasColor(aContext, aRed, aGreen, aBlue, aMessage) {
|
||||
let [r, g, b] = aContext.getImageData(0, 0, 1, 1).data;
|
||||
ok(r == aRed && g == aGreen && b == aBlue, aMessage);
|
||||
}
|
|
@ -141,7 +141,7 @@ TreePanel.prototype = {
|
|||
this.treeLoaded = true;
|
||||
this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
|
||||
this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
|
||||
this.treeIFrame.addEventListener("keypress", this.IUI, false);
|
||||
this.treeIFrame.focus();
|
||||
delete this.initializingTreePanel;
|
||||
Services.obs.notifyObservers(null,
|
||||
this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
|
||||
|
@ -233,7 +233,7 @@ TreePanel.prototype = {
|
|||
} catch(e) {
|
||||
treeBox.height = 112;
|
||||
}
|
||||
|
||||
|
||||
treeBox.minHeight = 64;
|
||||
treeBox.flex = 1;
|
||||
toolbarParent.insertBefore(treeBox, toolbar);
|
||||
|
@ -466,9 +466,6 @@ TreePanel.prototype = {
|
|||
editorInput.value = aAttrVal;
|
||||
editorInput.select();
|
||||
|
||||
// remove tree key navigation events
|
||||
this.treeIFrame.removeEventListener("keypress", this.IUI, false);
|
||||
|
||||
// listen for editor specific events
|
||||
this.bindEditorEvent(editor, "click", function(aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
|
@ -561,9 +558,6 @@ TreePanel.prototype = {
|
|||
this.editingContext = null;
|
||||
this.editingEvents = {};
|
||||
|
||||
// re-add navigation listener
|
||||
this.treeIFrame.addEventListener("keypress", this.IUI, false);
|
||||
|
||||
// event notification
|
||||
Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
|
||||
null);
|
||||
|
@ -700,7 +694,6 @@ TreePanel.prototype = {
|
|||
}
|
||||
|
||||
if (this.treeIFrame) {
|
||||
this.treeIFrame.removeEventListener("keypress", this.IUI, false);
|
||||
this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
|
||||
this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
|
||||
let parent = this.treeIFrame.parentNode;
|
||||
|
|
|
@ -698,12 +698,14 @@ Highlighter.prototype = {
|
|||
{
|
||||
this.browser.addEventListener("resize", this, true);
|
||||
this.browser.addEventListener("scroll", this, true);
|
||||
this.browser.addEventListener("MozAfterPaint", this, true);
|
||||
},
|
||||
|
||||
detachPageListeners: function Highlighter_detachPageListeners()
|
||||
{
|
||||
this.browser.removeEventListener("resize", this, true);
|
||||
this.browser.removeEventListener("scroll", this, true);
|
||||
this.browser.removeEventListener("MozAfterPaint", this, true);
|
||||
},
|
||||
|
||||
attachKeysListeners: function Highlighter_attachKeysListeners()
|
||||
|
@ -734,8 +736,10 @@ Highlighter.prototype = {
|
|||
this.handleMouseMove(aEvent);
|
||||
break;
|
||||
case "resize":
|
||||
case "scroll":
|
||||
this.computeZoomFactor();
|
||||
break;
|
||||
case "MozAfterPaint":
|
||||
case "scroll":
|
||||
this.brieflyDisableTransitions();
|
||||
this.invalidateSize();
|
||||
break;
|
||||
|
@ -745,7 +749,6 @@ Highlighter.prototype = {
|
|||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
break;
|
||||
case "keypress":
|
||||
switch (aEvent.keyCode) {
|
||||
case this.chromeWin.KeyEvent.DOM_VK_RETURN:
|
||||
|
@ -753,69 +756,6 @@ Highlighter.prototype = {
|
|||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
||||
let node;
|
||||
if (this.node) {
|
||||
node = this.node.parentNode;
|
||||
} else {
|
||||
node = this.defaultSelection;
|
||||
}
|
||||
if (node && this.isNodeHighlightable(node)) {
|
||||
this.highlight(node);
|
||||
}
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
||||
if (this.node) {
|
||||
// Find the first child that is highlightable.
|
||||
for (let i = 0; i < this.node.childNodes.length; i++) {
|
||||
node = this.node.childNodes[i];
|
||||
if (node && this.isNodeHighlightable(node)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node = this.defaultSelection;
|
||||
}
|
||||
if (node && this.isNodeHighlightable(node)) {
|
||||
this.highlight(node, true);
|
||||
}
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
||||
if (this.node) {
|
||||
// Find a previous sibling that is highlightable.
|
||||
node = this.node.previousSibling;
|
||||
while (node && !this.isNodeHighlightable(node)) {
|
||||
node = node.previousSibling;
|
||||
}
|
||||
} else {
|
||||
node = this.defaultSelection;
|
||||
}
|
||||
if (node && this.isNodeHighlightable(node)) {
|
||||
this.highlight(node, true);
|
||||
}
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
||||
if (this.node) {
|
||||
// Find a next sibling that is highlightable.
|
||||
node = this.node.nextSibling;
|
||||
while (node && !this.isNodeHighlightable(node)) {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
} else {
|
||||
node = this.defaultSelection;
|
||||
}
|
||||
if (node && this.isNodeHighlightable(node)) {
|
||||
this.highlight(node, true);
|
||||
}
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -286,6 +286,8 @@ InspectorUI.prototype = {
|
|||
|
||||
// initialize the highlighter
|
||||
this.highlighter = new Highlighter(this.chromeWin);
|
||||
|
||||
this.setupNavigationKeys();
|
||||
this.highlighterReady();
|
||||
},
|
||||
|
||||
|
@ -349,6 +351,36 @@ InspectorUI.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Browse nodes according to the breadcrumbs layout, only for some specific
|
||||
* elements of the UI.
|
||||
*/
|
||||
setupNavigationKeys: function IUI_setupNavigationKeys()
|
||||
{
|
||||
// UI elements that are arrow keys sensitive:
|
||||
// - highlighter veil;
|
||||
// - content window (when the highlighter `veil is pointer-events:none`;
|
||||
// - the Inspector toolbar.
|
||||
|
||||
this.onKeypress = this.onKeypress.bind(this);
|
||||
|
||||
this.highlighter.highlighterContainer.addEventListener("keypress",
|
||||
this.onKeypress, true);
|
||||
this.win.addEventListener("keypress", this.onKeypress, true);
|
||||
this.toolbar.addEventListener("keypress", this.onKeypress, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the event listeners for the arrowkeys.
|
||||
*/
|
||||
removeNavigationKeys: function IUI_removeNavigationKeys()
|
||||
{
|
||||
this.highlighter.highlighterContainer.removeEventListener("keypress",
|
||||
this.onKeypress, true);
|
||||
this.win.removeEventListener("keypress", this.onKeypress, true);
|
||||
this.toolbar.removeEventListener("keypress", this.onKeypress, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Close inspector UI and associated panels. Unhighlight and stop inspecting.
|
||||
* Remove event listeners for document scrolling, resize,
|
||||
|
@ -375,6 +407,8 @@ InspectorUI.prototype = {
|
|||
this.closing = true;
|
||||
this.toolbar.hidden = true;
|
||||
|
||||
this.removeNavigationKeys();
|
||||
|
||||
this.progressListener.destroy();
|
||||
delete this.progressListener;
|
||||
|
||||
|
@ -592,6 +626,14 @@ InspectorUI.prototype = {
|
|||
false);
|
||||
}
|
||||
break;
|
||||
case "keypress":
|
||||
switch (event.keyCode) {
|
||||
case this.chromeWin.KeyEvent.DOM_VK_ESCAPE:
|
||||
this.closeInspectorUI(false);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
}
|
||||
case "pagehide":
|
||||
win = event.originalTarget.defaultView;
|
||||
// Skip iframes/frames.
|
||||
|
@ -611,18 +653,66 @@ InspectorUI.prototype = {
|
|||
false);
|
||||
}
|
||||
break;
|
||||
case "keypress":
|
||||
switch (event.keyCode) {
|
||||
case this.chromeWin.KeyEvent.DOM_VK_ESCAPE:
|
||||
this.closeInspectorUI(false);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* handles "keypress" events.
|
||||
*/
|
||||
onKeypress: function IUI_onKeypress(event)
|
||||
{
|
||||
let node = null;
|
||||
let bc = this.breadcrumbs;
|
||||
switch (event.keyCode) {
|
||||
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
||||
if (bc.currentIndex != 0)
|
||||
node = bc.nodeHierarchy[bc.currentIndex - 1].node;
|
||||
if (node && this.highlighter.isNodeHighlightable(node))
|
||||
this.highlighter.highlight(node);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
||||
if (bc.currentIndex < bc.nodeHierarchy.length - 1)
|
||||
node = bc.nodeHierarchy[bc.currentIndex + 1].node;
|
||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
||||
this.highlighter.highlight(node);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
||||
if (this.selection) {
|
||||
// Find a previous sibling that is highlightable.
|
||||
node = this.selection.previousSibling;
|
||||
while (node && !this.highlighter.isNodeHighlightable(node)) {
|
||||
node = node.previousSibling;
|
||||
}
|
||||
}
|
||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
||||
this.highlighter.highlight(node, true);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
||||
if (this.selection) {
|
||||
// Find a next sibling that is highlightable.
|
||||
node = this.selection.nextSibling;
|
||||
while (node && !this.highlighter.isNodeHighlightable(node)) {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
}
|
||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
||||
this.highlighter.highlight(node, true);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//// CssRuleView methods
|
||||
|
||||
|
@ -1720,6 +1810,8 @@ HTMLBreadcrumbs.prototype = {
|
|||
}
|
||||
if (aIdx > -1) {
|
||||
this.nodeHierarchy[aIdx].button.setAttribute("checked", "true");
|
||||
if (this.hadFocus)
|
||||
this.nodeHierarchy[aIdx].button.focus();
|
||||
}
|
||||
this.currentIndex = aIdx;
|
||||
},
|
||||
|
@ -1895,6 +1987,10 @@ HTMLBreadcrumbs.prototype = {
|
|||
{
|
||||
this.menu.hidePopup();
|
||||
|
||||
let cmdDispatcher = this.IUI.chromeDoc.commandDispatcher;
|
||||
this.hadFocus = (cmdDispatcher.focusedElement &&
|
||||
cmdDispatcher.focusedElement.parentNode == this.container);
|
||||
|
||||
let selection = this.IUI.selection;
|
||||
let idx = this.indexOf(selection);
|
||||
|
||||
|
@ -1924,7 +2020,8 @@ HTMLBreadcrumbs.prototype = {
|
|||
|
||||
// Make sure the selected node and its neighbours are visible.
|
||||
this.scroll();
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -69,6 +69,7 @@ _BROWSER_FILES = \
|
|||
browser_inspector_changes.js \
|
||||
browser_inspector_ruleviewstore.js \
|
||||
browser_inspector_duplicate_ruleview.js \
|
||||
browser_inspector_invalidate.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let doc;
|
||||
let div;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
div = doc.createElement("div");
|
||||
div.setAttribute("style", "width: 100px; height: 100px;");
|
||||
doc.body.appendChild(div);
|
||||
|
||||
Services.obs.addObserver(runTest,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
InspectorUI.toggleInspectorUI();
|
||||
}
|
||||
|
||||
function runTest(subject)
|
||||
{
|
||||
Services.obs.removeObserver(runTest,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
|
||||
InspectorUI.highlighter.highlight(div);
|
||||
|
||||
executeSoon(function() {
|
||||
let veilBoxDims = InspectorUI.highlighter.veilTransparentBox;
|
||||
is(veilBoxDims.style.width, "100px", "selection has the right width");
|
||||
|
||||
div.style.width = "200px";
|
||||
setTimeout(function () {
|
||||
let veilBoxDims = InspectorUI.highlighter.veilTransparentBox;
|
||||
is(veilBoxDims.style.width, "200px", "selection updated");
|
||||
InspectorUI.closeInspectorUI();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
doc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,basic tests for inspector";
|
||||
}
|
||||
|
|
@ -92,8 +92,7 @@ function ruleViewOpened1()
|
|||
prop.setEnabled(false);
|
||||
|
||||
// Open second tab and switch to it
|
||||
tab2 = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab2;
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
|
||||
|
|
|
@ -37,7 +37,9 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
|
||||
let LayoutHelpers = tempScope.LayoutHelpers;
|
||||
|
||||
function isHighlighting()
|
||||
{
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
let NetUtil = tempScope.NetUtil;
|
||||
let FileUtils = tempScope.FileUtils;
|
||||
|
||||
// Reference to the Scratchpad chrome window object.
|
||||
let gScratchpadWindow;
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
let NetUtil = tempScope.NetUtil;
|
||||
let FileUtils = tempScope.FileUtils;
|
||||
|
||||
// only finish() when correct number of tests are done
|
||||
const expected = 5;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
function test()
|
||||
{
|
||||
|
|
|
@ -75,6 +75,7 @@ function runTests()
|
|||
sp.run();
|
||||
|
||||
is(window.foobarBug636725, "aloha2", "window.foobarBug636725 has been set");
|
||||
delete window.foobarBug636725;
|
||||
|
||||
sp.setText("gBrowser", 7);
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
let NetUtil = tempScope.NetUtil;
|
||||
let FileUtils = tempScope.FileUtils;
|
||||
|
||||
// Reference to the Scratchpad object.
|
||||
let gScratchpad;
|
||||
|
|
|
@ -69,11 +69,6 @@ function SplitView(aRoot)
|
|||
|
||||
this._mql = aRoot.ownerDocument.defaultView.matchMedia(LANDSCAPE_MEDIA_QUERY);
|
||||
|
||||
this._filter = aRoot.querySelector(".splitview-filter");
|
||||
if (this._filter) {
|
||||
this._setupFilterBox();
|
||||
}
|
||||
|
||||
// items list focus and search-on-type handling
|
||||
this._nav.addEventListener("keydown", function onKeyCatchAll(aEvent) {
|
||||
function getFocusedItemWithin(nav) {
|
||||
|
@ -116,13 +111,6 @@ function SplitView(aRoot)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// search-on-type when any non-whitespace character is pressed while list
|
||||
// has the focus
|
||||
if (this._filter &&
|
||||
!/\s/.test(String.fromCharCode(aEvent.which))) {
|
||||
this._filter.focus();
|
||||
}
|
||||
}.bind(this), false);
|
||||
}
|
||||
|
||||
|
@ -227,10 +215,6 @@ SplitView.prototype = {
|
|||
* Called when the item is hidden/inactive.
|
||||
* - function(summary, details, data) onDestroy
|
||||
* Called when the item has been removed.
|
||||
* - function(summary, details, data, query) onFilterBy
|
||||
* Called when the user performs a filtering search.
|
||||
* If the function returns false, the item does not match query
|
||||
* string and will be hidden.
|
||||
* - object data
|
||||
* Object to pass to the callbacks above.
|
||||
* - number ordinal
|
||||
|
@ -327,71 +311,6 @@ SplitView.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter items by given string.
|
||||
* Matching is performed on every item by calling onFilterBy when defined
|
||||
* and then by searching aQuery in the summary element's text item.
|
||||
* Non-matching item is hidden.
|
||||
*
|
||||
* If no item matches, 'splitview-all-filtered' class is set on the filter
|
||||
* input element and the splitview-nav element.
|
||||
*
|
||||
* @param string aQuery
|
||||
* The query string. Use null to reset (no filter).
|
||||
* @return number
|
||||
* The number of filtered (non-matching) item.
|
||||
*/
|
||||
filterItemsBy: function ASV_filterItemsBy(aQuery)
|
||||
{
|
||||
if (!this._nav.hasChildNodes()) {
|
||||
return 0;
|
||||
}
|
||||
if (aQuery) {
|
||||
aQuery = aQuery.trim();
|
||||
}
|
||||
if (!aQuery) {
|
||||
for (let i = 0; i < this._nav.childNodes.length; ++i) {
|
||||
this._nav.childNodes[i].classList.remove("splitview-filtered");
|
||||
}
|
||||
this._filter.classList.remove("splitview-all-filtered");
|
||||
this._nav.classList.remove("splitview-all-filtered");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
let filteredCount = 0;
|
||||
for (let i = 0; i < this._nav.childNodes.length; ++i) {
|
||||
let summary = this._nav.childNodes[i];
|
||||
|
||||
let matches = false;
|
||||
let binding = summary.getUserData(BINDING_USERDATA);
|
||||
if (binding.onFilterBy) {
|
||||
matches = binding.onFilterBy(summary, binding._details, binding.data, aQuery);
|
||||
}
|
||||
if (!matches) { // try text content
|
||||
let content = summary.textContent.toUpperCase();
|
||||
matches = (content.indexOf(aQuery.toUpperCase()) > -1);
|
||||
}
|
||||
|
||||
count++;
|
||||
if (!matches) {
|
||||
summary.classList.add("splitview-filtered");
|
||||
filteredCount++;
|
||||
} else {
|
||||
summary.classList.remove("splitview-filtered");
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0 && filteredCount == count) {
|
||||
this._filter.classList.add("splitview-all-filtered");
|
||||
this._nav.classList.add("splitview-all-filtered");
|
||||
} else {
|
||||
this._filter.classList.remove("splitview-all-filtered");
|
||||
this._nav.classList.remove("splitview-all-filtered");
|
||||
}
|
||||
return filteredCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the item's CSS class name.
|
||||
* This sets the class on both the summary and details elements, retaining
|
||||
|
@ -415,39 +334,4 @@ SplitView.prototype = {
|
|||
viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
|
||||
binding._details.className = viewSpecific + " " + aClassName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set up filter search box.
|
||||
*/
|
||||
_setupFilterBox: function ASV__setupFilterBox()
|
||||
{
|
||||
let clearFilter = function clearFilter(aEvent) {
|
||||
this._filter.value = "";
|
||||
this.filterItemsBy("");
|
||||
return false;
|
||||
}.bind(this);
|
||||
|
||||
this._filter.addEventListener("command", function onFilterInput(aEvent) {
|
||||
this.filterItemsBy(this._filter.value);
|
||||
}.bind(this), false);
|
||||
|
||||
this._filter.addEventListener("keyup", function onFilterKeyUp(aEvent) {
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
|
||||
clearFilter();
|
||||
}
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_ENTER ||
|
||||
aEvent.keyCode == aEvent.DOM_VK_RETURN) {
|
||||
// autofocus matching item if there is only one
|
||||
let matches = this._nav.querySelectorAll("* > li:not(.splitview-filtered)");
|
||||
if (matches.length == 1) {
|
||||
this.activeSummary = matches[0];
|
||||
}
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
let clearButtons = this._root.querySelectorAll(".splitview-filter-clearButton");
|
||||
for (let i = 0; i < clearButtons.length; ++i) {
|
||||
clearButtons[i].addEventListener("click", clearFilter, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -58,6 +58,7 @@ box,
|
|||
.splitview-controller {
|
||||
min-height: 3em;
|
||||
max-height: 14em;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.splitview-nav {
|
||||
|
@ -123,4 +124,8 @@ ol.splitview-nav > li.splitview-filtered {
|
|||
.splitview-portrait-resizer {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.splitview-controller {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
// Tests that our Promise implementation works properly
|
||||
|
||||
Cu.import("resource:///modules/devtools/Promise.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/Promise.jsm", tempScope);
|
||||
let Promise = tempScope.Promise;
|
||||
|
||||
function test() {
|
||||
addTab("about:blank", function() {
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
|
||||
// Tests that the DOM Template engine works properly
|
||||
|
||||
Cu.import("resource:///modules/devtools/Templater.jsm");
|
||||
Cu.import("resource:///modules/devtools/Promise.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/Templater.jsm", tempScope);
|
||||
Cu.import("resource:///modules/devtools/Promise.jsm", tempScope);
|
||||
let template = tempScope.template;
|
||||
let Promise = tempScope.Promise;
|
||||
|
||||
function test() {
|
||||
addTab("http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html", function() {
|
||||
|
|
|
@ -15,6 +15,8 @@ Orion version: git clone from 2011-12-09
|
|||
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=366312
|
||||
+ patch for Mozilla Bug 711737 - Orion should support all the CSS properties from CSS1, CSS2, CSS2.1 and CSS3
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=711737
|
||||
+ patch for Mozilla Bug 719028 - Style Editor does not highlight a few CSS2.0 and CSS3 properties
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=719028
|
||||
|
||||
# License
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Felipe Heidrich (IBM Corporation) - initial API and implementation
|
||||
* Silenio Quarti (IBM Corporation) - initial API and implementation
|
||||
* Mihai Sucan (Mozilla Foundation) - fix for Bug#364214
|
||||
* Alex Lakatos (Mozilla Contributor) - fix for Mozilla Bug#719028
|
||||
*/
|
||||
|
||||
/*global window */
|
||||
|
@ -10488,13 +10489,14 @@ define(['orion/textview/annotations'], function(mAnnotations) {
|
|||
"column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-width", "columns", "content", "counter-increment",
|
||||
"counter-reset", "crop", "cue", "cue-after", "cue-before", "cursor", "direction", "display", "dominant-baseline",
|
||||
"drop-initial-after-adjust", "drop-initial-after-align", "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
|
||||
"drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "float", "float-offset", "font", "font-family", "font-size",
|
||||
"font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation",
|
||||
"height", "hyphenate-after", "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon",
|
||||
"image-orientation", "image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height",
|
||||
"line-stacking", "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image",
|
||||
"list-style-position", "list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after",
|
||||
"mark-before", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
|
||||
"drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex-align", "flex-flow", "flex-inline-pack", "flex-order",
|
||||
"flex-pack", "float", "float-offset", "font", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style",
|
||||
"font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", "height", "hyphenate-after",
|
||||
"hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", "image-orientation",
|
||||
"image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height", "line-stacking",
|
||||
"line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", "list-style-position",
|
||||
"list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after", "mark-before",
|
||||
"marker-offset", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
|
||||
"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", "nav-up", "opacity", "orphans",
|
||||
"outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "overflow-x",
|
||||
"overflow-y", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "page", "page-break-after", "page-break-before",
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let editor;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", tempScope);
|
||||
let SourceEditor = tempScope.SourceEditor;
|
||||
|
||||
let testWin;
|
||||
let testDoc;
|
||||
|
|
|
@ -229,6 +229,8 @@ StyleEditor.prototype = {
|
|||
};
|
||||
|
||||
sourceEditor.init(aElement, config, function onSourceEditorReady() {
|
||||
setupBracketCompletion(sourceEditor);
|
||||
|
||||
sourceEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
function onTextChanged(aEvent) {
|
||||
this.updateStyleSheet();
|
||||
|
@ -1132,3 +1134,48 @@ function repeat(aText, aCount)
|
|||
{
|
||||
return (new Array(aCount + 1)).join(aText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up bracket completion on a given SourceEditor.
|
||||
* This automatically closes the following CSS brackets: "{", "(", "["
|
||||
*
|
||||
* @param SourceEditor aSourceEditor
|
||||
*/
|
||||
function setupBracketCompletion(aSourceEditor)
|
||||
{
|
||||
let editorElement = aSourceEditor.editorElement;
|
||||
let pairs = {
|
||||
123: { // {
|
||||
closeString: "}",
|
||||
closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
|
||||
},
|
||||
40: { // (
|
||||
closeString: ")",
|
||||
closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_0
|
||||
},
|
||||
91: { // [
|
||||
closeString: "]",
|
||||
closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
|
||||
},
|
||||
};
|
||||
|
||||
editorElement.addEventListener("keypress", function onKeyPress(aEvent) {
|
||||
let pair = pairs[aEvent.charCode];
|
||||
if (!pair) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We detected an open bracket, sending closing character
|
||||
let keyCode = pair.closeKeyCode;
|
||||
let charCode = pair.closeString.charCodeAt(0);
|
||||
let modifiers = 0;
|
||||
let utils = editorElement.ownerDocument.defaultView.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
|
||||
utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
|
||||
utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
|
||||
// and rewind caret
|
||||
aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1);
|
||||
}, false);
|
||||
}
|
||||
|
|
|
@ -402,7 +402,9 @@ StyleEditorChrome.prototype = {
|
|||
|
||||
this._view.setItemClassName(summary, aEditor.flags);
|
||||
|
||||
text(summary, ".stylesheet-name", aEditor.getFriendlyName());
|
||||
let label = summary.querySelector(".stylesheet-name > label");
|
||||
label.setAttribute("value", aEditor.getFriendlyName());
|
||||
|
||||
text(summary, ".stylesheet-title", aEditor.styleSheet.title || "");
|
||||
text(summary, ".stylesheet-rule-count",
|
||||
PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
* Paul Rouget <paul@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -64,22 +65,17 @@ li.error > .stylesheet-info > .stylesheet-more > .stylesheet-error-message {
|
|||
}
|
||||
|
||||
.stylesheet-name {
|
||||
/* clip the text at the beginning */
|
||||
display: -moz-box;
|
||||
direction: rtl;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.stylesheet-name > label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.splitview-nav > li > hgroup.stylesheet-info {
|
||||
-moz-box-pack: center;
|
||||
}
|
||||
|
||||
.splitview-nav:-moz-locale-dir(ltr) > li.unsaved > hgroup .stylesheet-name:before,
|
||||
.splitview-nav:-moz-locale-dir(rtl) > li.unsaved > hgroup .stylesheet-name:after {
|
||||
content: "* ";
|
||||
}
|
||||
|
||||
.stylesheet-enabled {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
@ -107,7 +103,7 @@ li:hover > hgroup > .stylesheet-more > h3 > .stylesheet-saveButton {
|
|||
|
||||
.stylesheet-more {
|
||||
-moz-box-flex: 1;
|
||||
-moz-box-direction: reverse;
|
||||
-moz-box-pack: end;
|
||||
}
|
||||
|
||||
.splitview-nav > li > hgroup.stylesheet-info {
|
||||
|
|
|
@ -54,8 +54,19 @@
|
|||
persist="screenX screenY width height sizemode">
|
||||
<xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
|
||||
<xul:commandset id="style-editor-commandset">
|
||||
<xul:command id="style-editor-cmd-close" oncommand="window.close();"/>
|
||||
</xul:commandset>
|
||||
|
||||
<xul:keyset id="style-editor-keyset">
|
||||
<xul:key id="style-editor-key-close"
|
||||
key="&closeCmd.key;"
|
||||
command="style-editor-cmd-close"
|
||||
modifiers="accel"/>
|
||||
</xul:keyset>
|
||||
|
||||
<xul:box id="style-editor-chrome" class="splitview-root loading">
|
||||
<xul:box class="splitview-controller" id="stylesheets-controller" persist="width height">
|
||||
<xul:box class="splitview-controller">
|
||||
<xul:box class="splitview-main">
|
||||
<xul:toolbar class="devtools-toolbar">
|
||||
<xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
|
||||
|
@ -68,14 +79,10 @@
|
|||
tooltiptext="&importButton.tooltip;"
|
||||
label="&importButton.label;"
|
||||
disabled="true"/>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:textbox class="splitview-filter devtools-searchinput"
|
||||
type="search" flex="1"
|
||||
tooltiptext="&searchInput.tooltip;"
|
||||
placeholder="&searchInput.placeholder;"/>
|
||||
</xul:toolbar>
|
||||
</xul:box>
|
||||
<xul:box class="splitview-nav-container">
|
||||
<xul:box id="splitview-resizer-target" class="splitview-nav-container"
|
||||
persist="width height">
|
||||
<ol class="splitview-nav" tabindex="0"></ol>
|
||||
<div class="splitview-nav placeholder empty">
|
||||
<p><strong>&noStyleSheet.label;</strong></p>
|
||||
|
@ -84,13 +91,6 @@
|
|||
class="style-editor-newButton">&noStyleSheet-tip-action.label;</a>
|
||||
&noStyleSheet-tip-end.label;</p>
|
||||
</div>
|
||||
<div class="splitview-nav placeholder all-filtered">
|
||||
<p><strong>&searchNoResults.label;</strong></p>
|
||||
<p>
|
||||
<a href="#"
|
||||
class="splitview-filter-clearButton">&searchClearButton.label;</a>
|
||||
</p>
|
||||
</div>
|
||||
</xul:box> <!-- .splitview-nav-container -->
|
||||
</xul:box> <!-- .splitview-controller -->
|
||||
<xul:box class="splitview-side-details"/>
|
||||
|
@ -101,7 +101,7 @@
|
|||
title="&visibilityToggle.tooltip;"
|
||||
accesskey="&saveButton.accesskey;"></a>
|
||||
<hgroup class="stylesheet-info">
|
||||
<h1><a class="stylesheet-name" href="#"></a></h1>
|
||||
<h1><a class="stylesheet-name" href="#"><xul:label crop="start"/></a></h1>
|
||||
<div class="stylesheet-more">
|
||||
<h3 class="stylesheet-title"></h3>
|
||||
<h3 class="stylesheet-rule-count"></h3>
|
||||
|
@ -116,12 +116,12 @@
|
|||
|
||||
<xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
|
||||
<xul:resizer class="splitview-portrait-resizer"
|
||||
dir="bottom"
|
||||
element="stylesheets-controller"/>
|
||||
dir="bottom"
|
||||
element="splitview-resizer-target"/>
|
||||
<xul:toolbar id="splitview-details-toolbar" class="devtools-toolbar">
|
||||
<xul:resizer class="splitview-landscape-resizer"
|
||||
dir="bottomend"
|
||||
element="stylesheets-controller"/>
|
||||
element="splitview-resizer-target"/>
|
||||
</xul:toolbar>
|
||||
<xul:box class="stylesheet-editor-input textbox"
|
||||
data-placeholder="&editorTextbox.placeholder;"/>
|
||||
|
|
|
@ -53,7 +53,6 @@ _BROWSER_TEST_FILES = \
|
|||
browser_styleeditor_pretty.js \
|
||||
browser_styleeditor_readonly.js \
|
||||
browser_styleeditor_reopen.js \
|
||||
browser_styleeditor_sv_filter.js \
|
||||
browser_styleeditor_sv_keynav.js \
|
||||
browser_styleeditor_sv_resize.js \
|
||||
four.html \
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
// http rather than chrome to improve coverage
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
|
||||
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
let tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
let FileUtils = tempScope.FileUtils;
|
||||
|
||||
const FILENAME = "styleeditor-import-test.css";
|
||||
const SOURCE = "body{background:red;}";
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ function testFirstStyleSheetEditor(aChrome, aEditor)
|
|||
ok(!summary.classList.contains("inline"),
|
||||
"first stylesheet UI does not have INLINE class");
|
||||
|
||||
let name = summary.querySelector(".stylesheet-name").textContent;
|
||||
let name = summary.querySelector(".stylesheet-name > label").getAttribute("value");
|
||||
is(name, "simple.css",
|
||||
"first stylesheet's name is `simple.css`");
|
||||
|
||||
|
@ -113,7 +113,7 @@ function testSecondStyleSheetEditor(aChrome, aEditor)
|
|||
ok(summary.classList.contains("inline"),
|
||||
"second stylesheet UI has INLINE class");
|
||||
|
||||
let name = summary.querySelector(".stylesheet-name").textContent;
|
||||
let name = summary.querySelector(".stylesheet-name > label").getAttribute("value");
|
||||
ok(/^<.*>$/.test(name),
|
||||
"second stylesheet's name is surrounded by `<>`");
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
const TRANSITION_CLASS = "moz-styleeditor-transitioning";
|
||||
|
||||
const TESTCASE_CSS_SOURCE = "body{background-color:red;";
|
||||
|
||||
function test()
|
||||
{
|
||||
|
@ -79,9 +79,13 @@ function testEditorAdded(aChrome, aEditor)
|
|||
is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
|
||||
"content's background color is initially white");
|
||||
|
||||
for each (let c in "body{background-color:red;}") {
|
||||
for each (let c in TESTCASE_CSS_SOURCE) {
|
||||
EventUtils.synthesizeKey(c, {}, gChromeWindow);
|
||||
}
|
||||
|
||||
is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
|
||||
"rule bracket has been auto-closed");
|
||||
|
||||
}, gChromeWindow) ;
|
||||
},
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@ const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html";
|
|||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
let tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
let FileUtils = tempScope.FileUtils;
|
||||
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run
|
||||
});
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function getFilteredItemsCount(nav)
|
||||
{
|
||||
let matches = nav.querySelectorAll("*.splitview-filtered");
|
||||
return matches ? matches.length : 0;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach});
|
||||
aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach});
|
||||
}
|
||||
|
||||
function onFirstEditorAttach(aEditor)
|
||||
{
|
||||
let filter = gChromeWindow.document.querySelector(".splitview-filter");
|
||||
// force the command event on input since it is not possible to disable
|
||||
// the search textbox's timeout.
|
||||
let forceCommandEvent = function forceCommandEvent() {
|
||||
let evt = gChromeWindow.document.createEvent("XULCommandEvent");
|
||||
evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false,
|
||||
false, false, null);
|
||||
filter.dispatchEvent(evt);
|
||||
}
|
||||
filter.addEventListener("input", forceCommandEvent, false);
|
||||
|
||||
let nav = gChromeWindow.document.querySelector(".splitview-nav");
|
||||
nav.focus();
|
||||
|
||||
is(getFilteredItemsCount(nav), 0,
|
||||
"there is 0 filtered item initially");
|
||||
|
||||
waitForFocus(function () {
|
||||
// Search [s] (type-on-search since we focused nav above - not filter directly)
|
||||
EventUtils.synthesizeKey("s", {}, gChromeWindow);
|
||||
|
||||
// the search space is "simple.css" and "inline stylesheet #1" (2 sheets)
|
||||
is(getFilteredItemsCount(nav), 0,
|
||||
"there is 0 filtered item if searching for 's'");
|
||||
|
||||
EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si]
|
||||
|
||||
is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered
|
||||
"there is 1 filtered item if searching for 's'");
|
||||
|
||||
// use uppercase to check that filtering is case-insensitive
|
||||
EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX]
|
||||
is(getFilteredItemsCount(nav), 2,
|
||||
"there is 2 filtered items if searching for 's'"); // no match
|
||||
|
||||
// clear the search
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow);
|
||||
|
||||
is(filter.value, "",
|
||||
"filter is back to empty");
|
||||
is(getFilteredItemsCount(nav), 0,
|
||||
"there is 0 filtered item when filter is empty again");
|
||||
|
||||
for each (let c in "inline") {
|
||||
EventUtils.synthesizeKey(c, {}, gChromeWindow);
|
||||
}
|
||||
|
||||
is(getFilteredItemsCount(nav), 1, // simple.css is filtered
|
||||
"there is 1 filtered item if searching for 'inline'");
|
||||
|
||||
// auto-select the only result (enter the editor)
|
||||
EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow);
|
||||
|
||||
filter.removeEventListener("input", forceCommandEvent, false);
|
||||
}, gChromeWindow);
|
||||
}
|
||||
|
||||
function onSecondEditorAttach(aEditor)
|
||||
{
|
||||
ok(aEditor.sourceEditor.hasFocus(),
|
||||
"second editor has been selected and focused automatically.");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -1310,6 +1310,7 @@ InplaceEditor.prototype = {
|
|||
prevent = true;
|
||||
this.cancelled = true;
|
||||
this.input.blur();
|
||||
aEvent.stopPropagation();
|
||||
} else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
|
||||
// No need for leading spaces here. This is particularly
|
||||
// noticable when adding a property: it's very natural to type
|
||||
|
|
|
@ -14,7 +14,6 @@ function createDocument()
|
|||
'<span class="matches">Some styled text</span>' +
|
||||
'</div>';
|
||||
doc.title = "Style Inspector key binding test";
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
|
||||
stylePanel.createPanel(false, function() {
|
||||
|
|
|
@ -9,7 +9,10 @@ let stylePanel;
|
|||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/test/browser_bug683672.html";
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssHtmlTree.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/CssHtmlTree.jsm", tempScope);
|
||||
let CssHtmlTree = tempScope.CssHtmlTree;
|
||||
let PropertyView = tempScope.PropertyView;
|
||||
|
||||
function test()
|
||||
{
|
||||
|
@ -22,7 +25,6 @@ function tabLoaded()
|
|||
{
|
||||
browser.removeEventListener("load", tabLoaded, true);
|
||||
doc = content.document;
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
// ok(StyleInspector.isEnabled, "style inspector preference is enabled");
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runTests, "StyleInspector-opened", false);
|
||||
|
|
|
@ -12,7 +12,6 @@ function createDocument()
|
|||
doc.body.innerHTML = "<div style='color:blue;'></div>";
|
||||
|
||||
doc.title = "Style Inspector Selector Text Test";
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
stylePanel = new StyleInspector(window);
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
// Test that inherited properties are treated correctly.
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssLogic.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope);
|
||||
let CssLogic = tempScope.CssLogic;
|
||||
|
||||
let doc;
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
||||
let tempScope = {}
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
|
||||
let CssRuleView = tempScope.CssRuleView;
|
||||
let _ElementStyle = tempScope._ElementStyle;
|
||||
let _editableField = tempScope._editableField;
|
||||
|
||||
let doc = content.document;
|
||||
|
||||
|
@ -118,4 +122,4 @@ function test()
|
|||
}, true);
|
||||
|
||||
content.location = "data:text/html,inline editor tests";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
||||
let tempScope = {}
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
|
||||
let CssRuleView = tempScope.CssRuleView;
|
||||
let _ElementStyle = tempScope._ElementStyle;
|
||||
let _editableField = tempScope._editableField;
|
||||
|
||||
let doc;
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
||||
let tempScope = {}
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
|
||||
let CssRuleView = tempScope.CssRuleView;
|
||||
let _ElementStyle = tempScope._ElementStyle;
|
||||
let _editableField = tempScope._editableField;
|
||||
|
||||
let doc;
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
||||
let tempScope = {}
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
|
||||
let CssRuleView = tempScope.CssRuleView;
|
||||
let _ElementStyle = tempScope._ElementStyle;
|
||||
let _editableField = tempScope._editableField;
|
||||
|
||||
let doc;
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
||||
let tempScope = {}
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
|
||||
let CssRuleView = tempScope.CssRuleView;
|
||||
let _ElementStyle = tempScope._ElementStyle;
|
||||
let _editableField = tempScope._editableField;
|
||||
|
||||
let doc;
|
||||
let ruleDialog;
|
||||
|
|
|
@ -26,7 +26,6 @@ function createDocument()
|
|||
'<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
|
||||
'</div>';
|
||||
doc.title = "Style Inspector Test";
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
|
||||
stylePanel.createPanel(false, function() {
|
||||
|
|
|
@ -14,7 +14,6 @@ function createDocument()
|
|||
'<span id="matches" class="matches">Some styled text</span>' +
|
||||
'</div>';
|
||||
doc.title = "Style Inspector Search Filter Test";
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
// ok(StyleInspector.isEnabled, "style inspector preference is enabled");
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
|
||||
|
|
|
@ -14,7 +14,6 @@ function createDocument()
|
|||
'<span id="matches" class="matches">Some styled text</span>' +
|
||||
'</div>';
|
||||
doc.title = "Style Inspector Default Styles Test";
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
// ok(StyleInspector.isEnabled, "style inspector preference is enabled");
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
|
||||
|
|
|
@ -13,7 +13,6 @@ function createDocument()
|
|||
'.matches {color: #F00;}</style>' +
|
||||
'<span id="matches" class="matches">Some styled text</span>';
|
||||
doc.title = "Tests that the no results placeholder works properly";
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
|
||||
stylePanel.createPanel(false, function() {
|
||||
|
|
|
@ -35,8 +35,12 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
Cu.import("resource:///modules/devtools/StyleInspector.jsm");
|
||||
Cu.import("resource://gre/modules/HUDService.jsm");
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/StyleInspector.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/HUDService.jsm", tempScope);
|
||||
let StyleInspector = tempScope.StyleInspector;
|
||||
let HUDService = tempScope.HUDService;
|
||||
let ConsoleUtils = tempScope.ConsoleUtils;
|
||||
|
||||
function log(aMsg)
|
||||
{
|
||||
|
|
|
@ -156,10 +156,15 @@ Tilt.prototype = {
|
|||
|
||||
let controller = this.visualizers[aId].controller;
|
||||
let presenter = this.visualizers[aId].presenter;
|
||||
|
||||
TiltUtils.setDocumentZoom(presenter.transforms.zoom);
|
||||
|
||||
let content = presenter.contentWindow;
|
||||
let pageXOffset = content.pageXOffset * TiltUtils.getDocumentZoom();
|
||||
let pageYOffset = content.pageYOffset * TiltUtils.getDocumentZoom();
|
||||
|
||||
controller.removeEventListeners();
|
||||
controller.arcball.reset([-content.pageXOffset, -content.pageYOffset]);
|
||||
controller.arcball.reset([-pageXOffset, -pageYOffset]);
|
||||
presenter.executeDestruction(finalize.bind(this, aId));
|
||||
}
|
||||
},
|
||||
|
|
|
@ -672,6 +672,16 @@ TiltUtils.getDocumentZoom = function TU_getDocumentZoom() {
|
|||
.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the markup document viewer zoom for the currently selected browser.
|
||||
*
|
||||
* @param {Number} the zoom ammount
|
||||
*/
|
||||
TiltUtils.setDocumentZoom = function TU_getDocumentZoom(aZoom) {
|
||||
TiltUtils.getBrowserWindow()
|
||||
.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a garbage collection.
|
||||
*/
|
||||
|
|
|
@ -60,11 +60,12 @@ const INVISIBLE_ELEMENTS = {
|
|||
|
||||
const STACK_THICKNESS = 15;
|
||||
const WIREFRAME_COLOR = [0, 0, 0, 0.25];
|
||||
const INTRO_TRANSITION_DURATION = 80;
|
||||
const OUTRO_TRANSITION_DURATION = 50;
|
||||
const INTRO_TRANSITION_DURATION = 50;
|
||||
const OUTRO_TRANSITION_DURATION = 40;
|
||||
const INITIAL_Z_TRANSLATION = 400;
|
||||
|
||||
const MOUSE_CLICK_THRESHOLD = 10;
|
||||
const MOUSE_INTRO_DELAY = 10;
|
||||
const ARCBALL_SENSITIVITY = 0.5;
|
||||
const ARCBALL_ROTATION_STEP = 0.15;
|
||||
const ARCBALL_TRANSLATION_STEP = 35;
|
||||
|
@ -296,7 +297,7 @@ TiltVisualizer.Presenter = function TV_Presenter(
|
|||
|
||||
// call the attached ondraw event handler if specified (by the controller)
|
||||
if ("function" === typeof this.ondraw) {
|
||||
this.ondraw();
|
||||
this.ondraw(this.frames);
|
||||
}
|
||||
|
||||
if (!TiltVisualizer.Prefs.introTransition && !this.isExecutingDestruction) {
|
||||
|
@ -480,6 +481,11 @@ TiltVisualizer.Presenter.prototype = {
|
|||
this.maxTextureSize),
|
||||
format: "RGB"
|
||||
});
|
||||
|
||||
if ("function" === typeof this.onSetupTexture) {
|
||||
this.onSetupTexture();
|
||||
this.onSetupTexture = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -502,6 +508,9 @@ TiltVisualizer.Presenter.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
// save the mesh data for future use
|
||||
this.meshData = aData;
|
||||
|
||||
// create the visualization mesh using the vertices, texture coordinates
|
||||
// and indices computed when traversing the document object model
|
||||
this.meshStacks = {
|
||||
|
@ -524,19 +533,28 @@ TiltVisualizer.Presenter.prototype = {
|
|||
this.highlightNode(this.inspectorUI.selection);
|
||||
}
|
||||
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let width = Math.min(aData.meshWidth * zoom, renderer.width);
|
||||
let height = Math.min(aData.meshHeight * zoom, renderer.height);
|
||||
if (!this._initialMeshConfiguration) {
|
||||
this._initialMeshConfiguration = true;
|
||||
|
||||
// set the necessary mesh offsets
|
||||
this.transforms.offset[0] = -width * 0.5;
|
||||
this.transforms.offset[1] = -height * 0.5;
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let width = Math.min(aData.meshWidth * zoom, renderer.width);
|
||||
let height = Math.min(aData.meshHeight * zoom, renderer.height);
|
||||
|
||||
// make sure the canvas is opaque now that the initialization is finished
|
||||
this.canvas.style.background = TiltVisualizerStyle.canvas.background;
|
||||
// set the necessary mesh offsets
|
||||
this.transforms.offset[0] = -width * 0.5;
|
||||
this.transforms.offset[1] = -height * 0.5;
|
||||
|
||||
this.drawVisualization();
|
||||
this.redraw = true;
|
||||
// make sure the canvas is opaque now that the initialization is finished
|
||||
this.canvas.style.background = TiltVisualizerStyle.canvas.background;
|
||||
|
||||
this.drawVisualization();
|
||||
this.redraw = true;
|
||||
}
|
||||
|
||||
if ("function" === typeof this.onSetupMesh) {
|
||||
this.onSetupMesh();
|
||||
this.onSetupMesh = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -626,9 +644,16 @@ TiltVisualizer.Presenter.prototype = {
|
|||
* the current horizontal coordinate of the mouse
|
||||
* @param {Number} y
|
||||
* the current vertical coordinate of the mouse
|
||||
* @param {Object} aProperties
|
||||
* an object containing the following properties:
|
||||
* {Function} onpick: function to be called after picking succeeded
|
||||
* {Function} onfail: function to be called after picking failed
|
||||
*/
|
||||
highlightNodeAt: function TVP_highlightNodeAt(x, y)
|
||||
highlightNodeAt: function TVP_highlightNodeAt(x, y, aProperties)
|
||||
{
|
||||
// make sure the properties parameter is a valid object
|
||||
aProperties = aProperties || {};
|
||||
|
||||
// try to pick a mesh node using the current x, y coordinates
|
||||
this.pickNode(x, y, {
|
||||
|
||||
|
@ -638,6 +663,10 @@ TiltVisualizer.Presenter.prototype = {
|
|||
onfail: function TVP_onHighlightFail()
|
||||
{
|
||||
this.highlightNodeFor(-1);
|
||||
|
||||
if ("function" === typeof aProperties.onfail) {
|
||||
aProperties.onfail();
|
||||
}
|
||||
}.bind(this),
|
||||
|
||||
/**
|
||||
|
@ -649,6 +678,10 @@ TiltVisualizer.Presenter.prototype = {
|
|||
onpick: function TVP_onHighlightPick(aIntersection)
|
||||
{
|
||||
this.highlightNodeFor(aIntersection.index);
|
||||
|
||||
if ("function" === typeof aProperties.onpick) {
|
||||
aProperties.onpick();
|
||||
}
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
@ -701,6 +734,32 @@ TiltVisualizer.Presenter.prototype = {
|
|||
this.contentWindow.pageYOffset > 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes a node from the visualization mesh.
|
||||
*
|
||||
* @param {Number} aNodeIndex
|
||||
* the index of the node in the this.traverseData array;
|
||||
* if not specified, it will default to the current selection
|
||||
*/
|
||||
deleteNode: function TVP_deleteNode(aNodeIndex)
|
||||
{
|
||||
// we probably don't want to delete the html or body node.. just sayin'
|
||||
if ((aNodeIndex = aNodeIndex || this._currentSelection) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let renderer = this.renderer;
|
||||
let meshData = this.meshData;
|
||||
|
||||
for (let i = 0, k = 36 * aNodeIndex; i < 36; i++) {
|
||||
meshData.vertices[i + k] = 0;
|
||||
}
|
||||
|
||||
this.meshStacks.vertices = new renderer.VertexBuffer(meshData.vertices, 3);
|
||||
this.highlight.disabled = true;
|
||||
this.redraw = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Picks a stacked dom node at the x and y screen coordinates and issues
|
||||
* a callback function with the found intersection.
|
||||
|
@ -887,6 +946,9 @@ TiltVisualizer.Controller = function TV_Controller(aCanvas, aPresenter)
|
|||
this.width = aCanvas.width;
|
||||
this.height = aCanvas.height;
|
||||
|
||||
this.left *= TiltUtils.getDocumentZoom();
|
||||
this.top *= TiltUtils.getDocumentZoom();
|
||||
|
||||
/**
|
||||
* Arcball used to control the visualization using the mouse.
|
||||
*/
|
||||
|
@ -923,7 +985,6 @@ TiltVisualizer.Controller.prototype = {
|
|||
// bind commonly used mouse and keyboard events with the controller
|
||||
canvas.addEventListener("mousedown", this.onMouseDown, false);
|
||||
canvas.addEventListener("mouseup", this.onMouseUp, false);
|
||||
canvas.addEventListener("click", this.onMouseClick, false);
|
||||
canvas.addEventListener("mousemove", this.onMouseMove, false);
|
||||
canvas.addEventListener("mouseover", this.onMouseOver, false);
|
||||
canvas.addEventListener("mouseout", this.onMouseOut, false);
|
||||
|
@ -946,7 +1007,6 @@ TiltVisualizer.Controller.prototype = {
|
|||
|
||||
canvas.removeEventListener("mousedown", this.onMouseDown, false);
|
||||
canvas.removeEventListener("mouseup", this.onMouseUp, false);
|
||||
canvas.removeEventListener("click", this.onMouseClick, false);
|
||||
canvas.removeEventListener("mousemove", this.onMouseMove, false);
|
||||
canvas.removeEventListener("mouseover", this.onMouseOver, false);
|
||||
canvas.removeEventListener("mouseout", this.onMouseOut, false);
|
||||
|
@ -960,9 +1020,13 @@ TiltVisualizer.Controller.prototype = {
|
|||
|
||||
/**
|
||||
* Function called each frame, updating the visualization camera transforms.
|
||||
*
|
||||
* @param {Number} aFrames
|
||||
* the current animation frame count
|
||||
*/
|
||||
update: function TVC_update()
|
||||
update: function TVC_update(aFrames)
|
||||
{
|
||||
this.frames = aFrames;
|
||||
this.coordinates = this.arcball.update();
|
||||
|
||||
this.presenter.setRotation(this.coordinates.rotation);
|
||||
|
@ -978,11 +1042,16 @@ TiltVisualizer.Controller.prototype = {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.frames < MOUSE_INTRO_DELAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate x and y coordinates using using the client and target offset
|
||||
let button = e.which;
|
||||
this._downX = e.clientX - e.target.offsetLeft;
|
||||
this._downY = e.clientY - e.target.offsetTop;
|
||||
|
||||
this.arcball.mouseDown(this._downX, this._downY, e.which);
|
||||
this.arcball.mouseDown(this._downX, this._downY, button);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -993,34 +1062,24 @@ TiltVisualizer.Controller.prototype = {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.frames < MOUSE_INTRO_DELAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate x and y coordinates using using the client and target offset
|
||||
let button = e.which;
|
||||
let upX = e.clientX - e.target.offsetLeft;
|
||||
let upY = e.clientY - e.target.offsetTop;
|
||||
|
||||
this.arcball.mouseUp(upX, upY, button);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called every time a mouse button is clicked.
|
||||
*/
|
||||
onMouseClick: function TVC_onMouseClick(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// calculate x and y coordinates using using the client and target offset
|
||||
let button = e.which;
|
||||
let clickX = e.clientX - e.target.offsetLeft;
|
||||
let clickY = e.clientY - e.target.offsetTop;
|
||||
|
||||
// a click in Tilt is issued only when the mouse pointer stays in
|
||||
// relatively the same position
|
||||
if (Math.abs(this._downX - clickX) < MOUSE_CLICK_THRESHOLD &&
|
||||
Math.abs(this._downY - clickY) < MOUSE_CLICK_THRESHOLD) {
|
||||
if (Math.abs(this._downX - upX) < MOUSE_CLICK_THRESHOLD &&
|
||||
Math.abs(this._downY - upY) < MOUSE_CLICK_THRESHOLD) {
|
||||
|
||||
this.presenter.highlightNodeAt(clickX, clickY);
|
||||
this.presenter.highlightNodeAt(upX, upY);
|
||||
}
|
||||
|
||||
this.arcball.mouseUp(upX, upY, button);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1031,6 +1090,10 @@ TiltVisualizer.Controller.prototype = {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.frames < MOUSE_INTRO_DELAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate x and y coordinates using using the client and target offset
|
||||
let moveX = e.clientX - e.target.offsetLeft;
|
||||
let moveY = e.clientY - e.target.offsetTop;
|
||||
|
@ -1098,6 +1161,9 @@ TiltVisualizer.Controller.prototype = {
|
|||
this.presenter.tiltUI.destroy(this.presenter.tiltUI.currentWindowId, 1);
|
||||
return;
|
||||
}
|
||||
if (code === e.DOM_VK_X) {
|
||||
this.presenter.deleteNode();
|
||||
}
|
||||
|
||||
if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
|
@ -1195,21 +1261,21 @@ TiltVisualizer.Arcball = function TV_Arcball(
|
|||
*/
|
||||
this._lastRot = quat4.create();
|
||||
this._deltaRot = quat4.create();
|
||||
this._currentRot = quat4.create();
|
||||
this._currentRot = quat4.create(aInitialRot);
|
||||
|
||||
/**
|
||||
* The current camera translation coordinates.
|
||||
*/
|
||||
this._lastTrans = vec3.create();
|
||||
this._deltaTrans = vec3.create();
|
||||
this._currentTrans = vec3.create();
|
||||
this._currentTrans = vec3.create(aInitialTrans);
|
||||
this._zoomAmount = 0;
|
||||
|
||||
/**
|
||||
* Additional rotation and translation vectors.
|
||||
*/
|
||||
this._additionalRot = vec3.create(aInitialRot);
|
||||
this._additionalTrans = vec3.create(aInitialTrans);
|
||||
this._additionalRot = vec3.create();
|
||||
this._additionalTrans = vec3.create();
|
||||
this._deltaAdditionalRot = quat4.create();
|
||||
this._deltaAdditionalTrans = vec3.create();
|
||||
|
||||
|
@ -1617,21 +1683,22 @@ TiltVisualizer.Arcball.prototype = {
|
|||
*/
|
||||
reset: function TVA_reset(aFinalTranslation, aFinalRotation)
|
||||
{
|
||||
if ("function" === typeof this.onResetStart) {
|
||||
this.onResetStart();
|
||||
this.onResetStart = null;
|
||||
}
|
||||
|
||||
this.cancelMouseEvents();
|
||||
this.cancelKeyEvents();
|
||||
this._cancelResetInterval();
|
||||
|
||||
if (!this._resetInterval) {
|
||||
let window = TiltUtils.getBrowserWindow();
|
||||
let func = this._nextResetIntervalStep.bind(this);
|
||||
let window = TiltUtils.getBrowserWindow();
|
||||
let func = this._nextResetIntervalStep.bind(this);
|
||||
|
||||
vec3.zero(this._additionalTrans);
|
||||
vec3.zero(this._additionalRot);
|
||||
|
||||
this._save();
|
||||
this._resetFinalTranslation = vec3.create(aFinalTranslation);
|
||||
this._resetFinalRotation = quat4.create(aFinalRotation);
|
||||
this._resetInterval = window.setInterval(func, ARCBALL_RESET_INTERVAL);
|
||||
}
|
||||
this._save();
|
||||
this._resetFinalTranslation = vec3.create(aFinalTranslation);
|
||||
this._resetFinalRotation = quat4.create(aFinalRotation);
|
||||
this._resetInterval = window.setInterval(func, ARCBALL_RESET_INTERVAL);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1666,10 +1733,12 @@ TiltVisualizer.Arcball.prototype = {
|
|||
let r = quat4.multiply(quat4.inverse(quat4.create(this._currentRot)), fRot);
|
||||
|
||||
// reset the rotation quaternion and translation vector
|
||||
vec3.lerp(this._currentTrans, t, ARCBALL_RESET_FACTOR);
|
||||
vec3.lerp(this._currentTrans, t, ARCBALL_RESET_FACTOR / 4);
|
||||
quat4.slerp(this._currentRot, r, 1 - ARCBALL_RESET_FACTOR);
|
||||
|
||||
// also reset any additional transforms by the keyboard or mouse
|
||||
vec3.scale(this._additionalTrans, ARCBALL_RESET_FACTOR);
|
||||
vec3.scale(this._additionalRot, ARCBALL_RESET_FACTOR);
|
||||
this._zoomAmount *= ARCBALL_RESET_FACTOR;
|
||||
|
||||
// clear the loop if the all values are very close to zero
|
||||
|
@ -1678,7 +1747,9 @@ TiltVisualizer.Arcball.prototype = {
|
|||
vec3.length(vec3.subtract(this._currentRot, fRot, [])) < fDelta &&
|
||||
vec3.length(vec3.subtract(this._lastTrans, fTran, [])) < fDelta &&
|
||||
vec3.length(vec3.subtract(this._deltaTrans, fTran, [])) < fDelta &&
|
||||
vec3.length(vec3.subtract(this._currentTrans, fTran, [])) < fDelta) {
|
||||
vec3.length(vec3.subtract(this._currentTrans, fTran, [])) < fDelta &&
|
||||
vec3.length(this._additionalRot) < fDelta &&
|
||||
vec3.length(this._additionalTrans) < fDelta) {
|
||||
|
||||
this._cancelResetInterval();
|
||||
}
|
||||
|
|
|
@ -81,6 +81,11 @@ self.onmessage = function TWP_onMessage(event)
|
|||
let v2b = [v2f[0], v2f[1], v2f[2] - thickness];
|
||||
let v3b = [v3f[0], v3f[1], v3f[2] - thickness];
|
||||
|
||||
// don't do anything with degenerate quads
|
||||
if (!v0f[0] && !v1f[0] && !v2f[0] && !v3f[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// for each triangle in the stack box, check for the intersections
|
||||
if (self.intersect(v0f, v1f, v2f, ray, hit) || // front left
|
||||
self.intersect(v0f, v2f, v3f, ray, hit) || // front right
|
||||
|
|
|
@ -73,6 +73,11 @@ _BROWSER_TEST_FILES = \
|
|||
browser_tilt_math05.js \
|
||||
browser_tilt_math06.js \
|
||||
browser_tilt_math07.js \
|
||||
browser_tilt_picking.js \
|
||||
browser_tilt_picking_delete.js \
|
||||
browser_tilt_picking_highlight01.js \
|
||||
browser_tilt_picking_highlight02.js \
|
||||
browser_tilt_picking_highlight03.js \
|
||||
browser_tilt_utils01.js \
|
||||
browser_tilt_utils02.js \
|
||||
browser_tilt_utils03.js \
|
||||
|
|
|
@ -16,6 +16,7 @@ function test() {
|
|||
return;
|
||||
}
|
||||
|
||||
requestLongerTimeout(10);
|
||||
waitForExplicitFinish();
|
||||
Services.prefs.setBoolPref("accessibility.typeaheadfind", true);
|
||||
|
||||
|
@ -65,6 +66,10 @@ function performTest(canvas, arcball, callback) {
|
|||
executeSoon(function() {
|
||||
info("Synthesizing arcball reset key press.");
|
||||
|
||||
arcball.onResetStart = function() {
|
||||
info("Starting arcball reset animation.");
|
||||
};
|
||||
|
||||
arcball.onResetFinish = function() {
|
||||
ok(isApproxVec(arcball._lastRot, [0, 0, 0, 1]),
|
||||
"The arcball _lastRot field wasn't reset correctly.");
|
||||
|
|
|
@ -16,6 +16,7 @@ function test() {
|
|||
return;
|
||||
}
|
||||
|
||||
requestLongerTimeout(10);
|
||||
waitForExplicitFinish();
|
||||
|
||||
createTab(function() {
|
||||
|
@ -63,6 +64,10 @@ function performTest(canvas, arcball, callback) {
|
|||
executeSoon(function() {
|
||||
info("Synthesizing arcball reset key press.");
|
||||
|
||||
arcball.onResetStart = function() {
|
||||
info("Starting arcball reset animation.");
|
||||
};
|
||||
|
||||
arcball.onResetFinish = function() {
|
||||
ok(isApproxVec(arcball._lastRot, [0, 0, 0, 1]),
|
||||
"The arcball _lastRot field wasn't reset correctly.");
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping picking test because Tilt isn't enabled.");
|
||||
return;
|
||||
}
|
||||
if (!isWebGLSupported()) {
|
||||
info("Skipping picking test because WebGL isn't supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
createTab(function() {
|
||||
createTilt({
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
let presenter = instance.presenter;
|
||||
let canvas = presenter.canvas;
|
||||
|
||||
presenter.onSetupMesh = function() {
|
||||
|
||||
presenter.pickNode(canvas.width / 2, canvas.height / 2, {
|
||||
onpick: function(data)
|
||||
{
|
||||
ok(data.index > 0,
|
||||
"Simply picking a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After only picking a node, it shouldn't be highlighted.");
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping picking delete test because Tilt isn't enabled.");
|
||||
return;
|
||||
}
|
||||
if (!isWebGLSupported()) {
|
||||
info("Skipping picking delete test because WebGL isn't supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
createTab(function() {
|
||||
createTilt({
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
let presenter = instance.presenter;
|
||||
let canvas = presenter.canvas;
|
||||
|
||||
presenter.onSetupMesh = function() {
|
||||
|
||||
presenter.highlightNodeAt(canvas.width / 2, canvas.height / 2, {
|
||||
onpick: function()
|
||||
{
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
presenter.deleteNode();
|
||||
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Deleting a node shouldn't change the current selection.");
|
||||
ok(presenter.highlight.disabled,
|
||||
"After deleting a node, it shouldn't be highlighted.");
|
||||
|
||||
let nodeIndex = presenter._currentSelection;
|
||||
let meshData = presenter.meshData;
|
||||
|
||||
for (let i = 0, k = 36 * nodeIndex; i < 36; i++) {
|
||||
is(meshData.vertices[i + k], 0,
|
||||
"The stack vertices weren't degenerated properly.");
|
||||
}
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче