Merge mozilla-central into services-central

This commit is contained in:
Gregory Szorc 2012-09-18 11:37:04 -07:00
commit fadf7fd538
1445 changed files with 31393 additions and 18114 deletions

1071
AUTHORS Normal file

File diff suppressed because it is too large Load Diff

View File

@ -997,18 +997,19 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aNode);
// If the element is focusable or global ARIA attribute is applied to it or
// it is referenced by ARIA relationship then treat role="presentation" on
// the element as the role is not there.
if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::presentation)) {
// Ignore presentation role if element is focusable (focus event shouldn't
// be ever lost and should be sensible).
if (content->IsFocusable())
roleMapEntry = nullptr;
else
if (!content->IsFocusable() && !HasUniversalAriaProperty(content) &&
!HasRelatedContent(content))
return nullptr;
roleMapEntry = nullptr;
}
if (weakFrame.IsAlive() && !newAcc && isHTML) { // HTML accessibles
bool tryTagNameOrFrame = true;
nsIAtom *frameType = weakFrame.GetFrame()->GetType();
bool partOfHTMLTable =
@ -1016,6 +1017,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
frameType == nsGkAtoms::tableCellFrame ||
frameType == nsGkAtoms::tableRowGroupFrame ||
frameType == nsGkAtoms::tableRowFrame;
bool legalPartOfHTMLTable = partOfHTMLTable;
if (partOfHTMLTable) {
// Table-related frames don't get table-related roles
@ -1057,26 +1059,26 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
// otherwise create ARIA based accessible.
tryTagNameOrFrame = false;
legalPartOfHTMLTable = false;
break;
}
if (tableContent->Tag() == nsGkAtoms::table) {
// Stop before we are fooled by any additional table ancestors
// This table cell frameis part of a separate ancestor table.
tryTagNameOrFrame = false;
legalPartOfHTMLTable = false;
break;
}
}
if (!tableContent)
tryTagNameOrFrame = false;
legalPartOfHTMLTable = false;
}
if (roleMapEntry) {
// Create ARIA grid/treegrid accessibles if node is not of a child or
// valid child of HTML table and is not a HTML table.
if ((!partOfHTMLTable || !tryTagNameOrFrame) &&
// Create ARIA grid/treegrid accessibles if node is not a child or legal
// child of HTML table and is not a HTML table.
if ((!partOfHTMLTable || !legalPartOfHTMLTable) &&
frameType != nsGkAtoms::tableOuterFrame) {
if (roleMapEntry->role == roles::TABLE ||
@ -1091,16 +1093,16 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
}
if (!newAcc && tryTagNameOrFrame) {
if (!newAcc) {
// Prefer to use markup (mostly tag name, perhaps attributes) to
// decide if and what kind of accessible to create.
// The method creates accessibles for table related content too therefore
// we do not call it if accessibles for table related content are
// prevented above.
newAcc = CreateHTMLAccessibleByMarkup(weakFrame.GetFrame(), content,
docAcc);
docAcc, legalPartOfHTMLTable);
if (!newAcc) {
if (!newAcc && (!partOfHTMLTable || legalPartOfHTMLTable)) {
// Do not create accessible object subtrees for non-rendered table
// captions. This could not be done in
// nsTableCaptionFrame::GetAccessible() because the descendants of
@ -1553,8 +1555,20 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
already_AddRefed<Accessible>
nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
nsIContent* aContent,
DocAccessible* aDoc)
DocAccessible* aDoc,
bool aIsLegalPartOfHTMLTable)
{
if (aIsLegalPartOfHTMLTable) {
if (nsCoreUtils::IsHTMLTableHeader(aContent)) {
Accessible* accessible =
new HTMLTableHeaderCellAccessibleWrap(aContent, aDoc);
NS_IF_ADDREF(accessible);
return accessible;
}
return nullptr;
}
// This method assumes we're in an HTML namespace.
nsIAtom* tag = aContent->Tag();
if (tag == nsGkAtoms::figcaption) {
@ -1635,12 +1649,6 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
return accessible;
}
if (nsCoreUtils::IsHTMLTableHeader(aContent)) {
Accessible* accessible = new HTMLTableHeaderCellAccessibleWrap(aContent, aDoc);
NS_IF_ADDREF(accessible);
return accessible;
}
if (tag == nsGkAtoms::output) {
Accessible* accessible = new HTMLOutputAccessible(aContent, aDoc);
NS_IF_ADDREF(accessible);

View File

@ -229,7 +229,8 @@ private:
*/
already_AddRefed<Accessible>
CreateHTMLAccessibleByMarkup(nsIFrame* aFrame, nsIContent* aContent,
DocAccessible* aDoc);
DocAccessible* aDoc,
bool aIsLegalPartOfHTMLTable);
/**
* Create accessible if parent is a deck frame.

View File

@ -18,7 +18,7 @@ nsEventShell::FireEvent(AccEvent* aEvent)
return;
Accessible* accessible = aEvent->GetAccessible();
NS_ENSURE_TRUE(accessible,);
NS_ENSURE_TRUE_VOID(accessible);
nsINode* node = aEvent->GetNode();
if (node) {
@ -35,7 +35,7 @@ void
nsEventShell::FireEvent(uint32_t aEventType, Accessible* aAccessible,
EIsFromUserInput aIsFromUserInput)
{
NS_ENSURE_TRUE(aAccessible,);
NS_ENSURE_TRUE_VOID(aAccessible);
nsRefPtr<AccEvent> event = new AccEvent(aEventType, aAccessible,
aIsFromUserInput);

View File

@ -43,6 +43,7 @@
#include "nsIForm.h"
#include "nsIFormControl.h"
#include "nsDeckFrame.h"
#include "nsLayoutUtils.h"
#include "nsIPresShell.h"
#include "nsIStringBundle.h"
@ -606,26 +607,35 @@ Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
uint64_t
Accessible::VisibilityState()
{
uint64_t vstates = states::INVISIBLE | states::OFFSCREEN;
nsIFrame* frame = GetFrame();
if (!frame)
return vstates;
return states::INVISIBLE;
nsIPresShell* shell(mDoc->PresShell());
if (!shell)
return vstates;
// Walk the parent frame chain to see if there's invisible parent or the frame
// is in background tab.
if (!frame->GetStyleVisibility()->IsVisible())
return states::INVISIBLE;
// We need to know if at least a kMinPixels around the object is visible,
// otherwise it will be marked states::OFFSCREEN.
const uint16_t kMinPixels = 12;
const nsSize frameSize = frame->GetSize();
const nsRectVisibility rectVisibility =
shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
nsPresContext::CSSPixelsToAppUnits(kMinPixels));
nsIFrame* curFrame = frame;
do {
nsIView* view = curFrame->GetView();
if (view && view->GetVisibility() == nsViewVisibility_kHide)
return states::INVISIBLE;
if (rectVisibility == nsRectVisibility_kVisible)
vstates &= ~states::OFFSCREEN;
// Offscreen state for background tab content.
nsIFrame* parentFrame = curFrame->GetParent();
nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
if (deckFrame && deckFrame->GetSelectedBox() != curFrame)
return states::OFFSCREEN;
if (!parentFrame) {
parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
if (parentFrame && !parentFrame->GetStyleVisibility()->IsVisible())
return states::INVISIBLE;
}
curFrame = parentFrame;
} while (curFrame);
// Zero area rects can occur in the first frame of a multi-frame text flow,
// in which case the rendered text is not empty and the frame should not be
@ -638,16 +648,21 @@ Accessible::VisibilityState()
nsAutoString renderedText;
frame->GetRenderedText(&renderedText, nullptr, nullptr, 0, 1);
if (renderedText.IsEmpty())
return vstates;
return states::INVISIBLE;
}
// XXX Do we really need to cross from content to chrome ancestor?
if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY))
return vstates;
// We need to know if at least a kMinPixels around the object is visible,
// otherwise it will be marked states::OFFSCREEN.
const uint16_t kMinPixels = 12;
const nsSize frameSize = frame->GetSize();
const nsRectVisibility rectVisibility =
mDoc->PresShell()->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
nsPresContext::CSSPixelsToAppUnits(kMinPixels));
// Assume we are visible enough.
return vstates &= ~states::INVISIBLE;
if (rectVisibility != nsRectVisibility_kVisible)
return states::OFFSCREEN;
return 0;
}
uint64_t
@ -2929,7 +2944,7 @@ void
Accessible::CacheChildren()
{
DocAccessible* doc = Document();
NS_ENSURE_TRUE(doc,);
NS_ENSURE_TRUE_VOID(doc);
nsAccTreeWalker walker(doc, mContent, CanHaveAnonChildren());

View File

@ -1705,7 +1705,7 @@ HyperTextAccessible::GetSelectionDOMRanges(int16_t aType,
uint32_t childCount = startNode->GetChildCount();
nsresult rv = domSel->
GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
NS_ENSURE_SUCCESS(rv,);
NS_ENSURE_SUCCESS_VOID(rv);
// Remove collapsed ranges
uint32_t numRanges = aRanges->Length();

View File

@ -159,7 +159,7 @@ HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
inputElms = NS_GetContentList(formElm, namespaceId, tagName);
else
inputElms = NS_GetContentList(mContent->OwnerDoc(), namespaceId, tagName);
NS_ENSURE_TRUE(inputElms, );
NS_ENSURE_TRUE_VOID(inputElms);
uint32_t inputCount = inputElms->Length(false);

View File

@ -139,7 +139,7 @@ XULColorPickerAccessible::AreItemsOperable() const
void
XULColorPickerAccessible::CacheChildren()
{
NS_ENSURE_TRUE(mDoc,);
NS_ENSURE_TRUE_VOID(mDoc);
nsAccTreeWalker walker(mDoc, mContent, true);

View File

@ -181,7 +181,7 @@ XULButtonAccessible::CacheChildren()
mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::menuButton, eCaseMatters);
NS_ENSURE_TRUE(mDoc,);
NS_ENSURE_TRUE_VOID(mDoc);
if (!isMenu && !isMenuButton)
return;
@ -823,7 +823,7 @@ XULTextFieldAccessible::GetEditor() const
void
XULTextFieldAccessible::CacheChildren()
{
NS_ENSURE_TRUE(mDoc,);
NS_ENSURE_TRUE_VOID(mDoc);
// Create child accessibles for native anonymous content of underlying HTML
// input element.
nsCOMPtr<nsIContent> inputContent(GetInputField());

View File

@ -16,27 +16,56 @@
src="../role.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript"
src="../browser.js"></script>
<script type="application/javascript">
function doTest()
function addTab(aURL)
{
testStates("div", 0, 0, STATE_INVISIBLE);
this.eventSeq = [
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
];
this.invoke = function addTab_invoke()
{
tabBrowser().loadOneTab(aURL, null, "", null, false);
}
this.finalCheck = function addTab_finalCheck()
{
var tabDoc = tabDocumentAt(0);
var input = getAccessible(tabDoc.getElementById("input"));
testStates(input, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
}
this.getID = function addTab_getID()
{
return "add tab: " + aURL;
}
}
var gInputDocURI = "data:text/html,<html><input id='input'></html>";
function doTests()
{
testStates("div", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
testStates("div_off", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
testStates("div_abschild", 0, 0, STATE_INVISIBLE);
testStates("div_abschild", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
// Confirm destruction of accessibles.
document.getElementById("div").style.visibility = "hidden";
document.getElementById("div_off").style.visibility="hidden";
document.getElementById("div_abschild").style.visibility="hidden";
document.body.clientWidth; // flush layout
testAccessibleTree("outer_div", {children:[]});
gQueue = new eventQueue();
// Accessibles in background tab should have offscreen state and no
// invisible state.
gQueue.push(new addTab("about:blank"));
SimpleTest.finish();
gQueue.onFinish = function() { closeBrowserWindow(); }
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
openBrowserWindow(doTests, gInputDocURI);
</script>
</head>

View File

@ -17,6 +17,7 @@ MOCHITEST_A11Y_FILES =\
test_aria_globals.html \
test_aria_imgmap.html \
test_aria_presentation.html \
test_brokencontext.html \
test_button.xul \
test_canvas.html \
test_combobox.xul \

View File

@ -54,6 +54,19 @@
] };
testAccessibleTree("tblfocusable_cnt", tree);
// Has ARIA globals or referred by ARIA relationship.
tree =
{ SECTION: [ // container
{ LABEL: [ // label, has aria-owns
{ TEXT_LEAF: [ ] }
] },
{ TEXT_LEAF: [ ] },
{ LABEL: [ // label, referenced by aria-owns
{ TEXT_LEAF: [ ] }
] }
] };
testAccessibleTree("airaglobalprop_cnt", tree);
SimpleTest.finish();
}
@ -98,5 +111,10 @@
</table>
</div>
<div id="airaglobalprop_cnt">
<label role="presentation" aria-owns="ariaowned">has aria-owns</label>
<label role="presentation" id="ariaowned">referred by aria-owns</label>
</div>
</body>
</html>

View File

@ -0,0 +1,106 @@
<!DOCTYPE html>
<html>
<head>
<title>Broken context hierarchy</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript">
function doTest()
{
////////////////////////////////////////////////////////////////////////////
// HTML table elements outside table context.
ok(!isAccessible("tr_in_presentation_table"), "tr shouldn't be accessible");
ok(!isAccessible("th_in_presentation_table"), "th shouldn't be accessible");
ok(!isAccessible("td_in_presentation_table"), "td shouldn't be accessible");
var tree =
{ PUSHBUTTON: [ // table
{ NOTHING: [ // tbody
{ NOTHING: [ // tr
{ NOTHING: [ // th
{ TEXT_LEAF: [ ] }
] },
{ NOTHING: [ // td
{ TEXT_LEAF: [ ] }
] }
] },
] },
] };
testAccessibleTree("button_table", tree);
////////////////////////////////////////////////////////////////////////////
// Styled as HTML table elements, accessible is created by tag name
tree =
{ LINK: [ // a
{ TEXT_LEAF: [ ] }
] };
testAccessibleTree("a_as_td", tree);
tree =
{ HEADING: [
{ TEXT_LEAF: [ ] }
] };
testAccessibleTree("h1_as_td", tree);
testAccessibleTree("h2_as_td", tree);
testAccessibleTree("h3_as_td", tree);
testAccessibleTree("h4_as_td", tree);
testAccessibleTree("h5_as_td", tree);
testAccessibleTree("h6_as_td", tree);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=706849"
title="Create accessible by tag name as fallback if table descendant style is used out of table context">
Mozilla Bug 706849
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<!-- HTML table elements out of table -->
<table role="presentation">
<tr id="tr_in_presentation_table">
<th id="th_in_presentation_table">not a header</th>
<td id="td_in_presentation_table">not a cell</td>
</tr>
</table>
<table role="button" id="button_table">
<tr id="tr_in_button_table">
<th id="th_in_button_table">not a header</th>
<td id="td_in_button_table">not a cell</td>
</tr>
</table>
<!-- styled as HTML table elements -->
<a id="a_as_td" style="display:table-cell;" href="http://www.google.com">Google</a>
<h1 id="h1_as_td" style="display: table-cell;">h1</h1>
<h2 id="h2_as_td" style="display: table-cell;">h2</h2>
<h3 id="h3_as_td" style="display: table-cell;">h3</h3>
<h4 id="h4_as_td" style="display: table-cell;">h4</h4>
<h5 id="h5_as_td" style="display: table-cell;">h5</h5>
<h6 id="h6_as_td" style="display: table-cell;">h6</h6>
</body>
</html>

View File

@ -139,6 +139,7 @@
] } ]
};
testAccessibleTree("table4", accTree);
SimpleTest.finish();
}

2
aclocal.m4 vendored
View File

@ -17,6 +17,8 @@ builtin(include, build/autoconf/mozcommonheader.m4)dnl
builtin(include, build/autoconf/acwinpaths.m4)dnl
builtin(include, build/autoconf/lto.m4)dnl
builtin(include, build/autoconf/gcc-pr49911.m4)dnl
builtin(include, build/autoconf/gcc-pr39608.m4)dnl
builtin(include, build/autoconf/llvm-pr8927.m4)dnl
builtin(include, build/autoconf/frameptr.m4)dnl
builtin(include, build/autoconf/compiler-opts.m4)dnl
builtin(include, build/autoconf/expandlibs.m4)dnl

View File

@ -435,22 +435,30 @@ pref("marionette.defaultPrefs.port", 2828);
#endif
#ifdef MOZ_UPDATER
// Timeout before the update prompt automatically installs the update
pref("b2g.update.apply-prompt-timeout", 60000); // milliseconds
// Optional timeout the user can wait before getting another update prompt
pref("b2g.update.apply-wait-timeout", 1800000); // milliseconds
// Amount of time the updater waits for the process to exit cleanly before
// forcefully exiting the process
pref("b2g.update.self-destruct-timeout", 5000); // milliseconds
pref("app.update.enabled", true);
pref("app.update.auto", true);
pref("app.update.silent", true);
pref("app.update.auto", false);
pref("app.update.silent", false);
pref("app.update.mode", 0);
pref("app.update.incompatible.mode", 0);
pref("app.update.staging.enabled", true);
pref("app.update.service.enabled", true);
// The URL hosting the update manifest.
pref("app.update.url", "http://update.boot2gecko.org/m2.5/updates.xml");
pref("app.update.url", "http://update.boot2gecko.org/nightly/update.xml");
// Interval at which update manifest is fetched. In units of seconds.
pref("app.update.interval", 3600); // 1 hour
pref("app.update.interval", 86400); // 1 day
// First interval to elapse before checking for update. In units of
// milliseconds. Capped at 10 seconds.
pref("app.update.timerFirstInterval", 30000);
pref("app.update.timerMinimumDelay", 30); // seconds
pref("app.update.timerFirstInterval", 3600000); // 1 hour
pref("app.update.timerMinimumDelay", 3600); // 1 hour in seconds
// Don't throttle background updates.
pref("app.update.download.backgroundInterval", 0);
@ -484,7 +492,7 @@ pref("dom.experimental_forms", true);
// Turns on gralloc-based direct texturing for Gonk
pref("gfx.gralloc.enabled", false);
// XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
// XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
pref("javascript.options.mem.log", true);
// Increase mark slice time from 10ms to 30ms
@ -505,9 +513,9 @@ pref("ui.showHideScrollbars", 1);
// background.
pref("dom.ipc.processPriorityManager.enabled", true);
pref("dom.ipc.processPriorityManager.gracePeriodMS", 1000);
pref("hal.processPriorityManager.gonk.masterOomAdjust", 0);
pref("hal.processPriorityManager.gonk.foregroundOomAdjust", 1);
pref("hal.processPriorityManager.gonk.backgroundOomAdjust", 6);
pref("hal.processPriorityManager.gonk.masterOomScoreAdjust", 0);
pref("hal.processPriorityManager.gonk.foregroundOomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.backgroundOomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.masterNice", -1);
pref("hal.processPriorityManager.gonk.foregroundNice", 0);
pref("hal.processPriorityManager.gonk.backgroundNice", 10);

View File

@ -185,7 +185,7 @@ select > button {
background-color: transparent;
background-position: -15px center, 4px center !important;
background-repeat: no-repeat, no-repeat !important;
background-size: 100% 90%, normal normal;
background-size: 100% 90%, auto auto;
-moz-binding: none !important;
position: relative !important;

View File

@ -106,3 +106,8 @@ SettingsListener.observe('debug.log-animations.enabled', false, function(value)
SettingsListener.observe('debug.dev-mode', false, function(value) {
Services.prefs.setBoolPref('dom.mozApps.dev_mode', value);
});
// =================== Privacy ====================
SettingsListener.observe('privacy.donottrackheader.enabled', false, function(value) {
Services.prefs.setBoolPref('privacy.donottrackheader.enabled', value);
});

View File

@ -1,11 +1,6 @@
# Scrollbars
category agent-style-sheets browser-content-stylesheet chrome://browser/content/content.css
# CameraContent.js
component {eff4231b-abce-4f7f-a40a-d646e8fde3ce} CameraContent.js
contract @mozilla.org/b2g-camera-content;1 {eff4231b-abce-4f7f-a40a-d646e8fde3ce}
category JavaScript-navigator-property mozCamera @mozilla.org/b2g-camera-content;1
# AlertsService.js
component {fe33c107-82a4-41d6-8c64-5353267e04c9} AlertsService.js
contract @mozilla.org/system-alerts-service;1 {fe33c107-82a4-41d6-8c64-5353267e04c9}
@ -46,3 +41,7 @@ contract @mozilla.org/uriloader/content-handler;1?type=application/pdf {d18d0216
# PaymentGlue.js
component {8b83eabc-7929-47f4-8b48-4dea8d887e4b} PaymentGlue.js
contract @mozilla.org/payment/ui-glue;1 {8b83eabc-7929-47f4-8b48-4dea8d887e4b}
# YoutubeProtocolHandler.js
component {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad} YoutubeProtocolHandler.js
contract @mozilla.org/network/protocol;1?name=vnd.youtube {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}

View File

@ -1,83 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const kProtocolName = "b2g-camera:";
let CameraContent = function() {
this.hasPrivileges = false;
this.mapping = [];
}
CameraContent.prototype = {
getCameraURI: function(aOptions) {
if (!this.hasPrivileges)
return null;
let options = aOptions || { };
if (!options.camera)
options.camera = 0;
if (!options.width)
options.width = 320;
if (!options.height)
options.height = 240;
let uuid = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
uuid = uuid.substring(1, uuid.length - 2); // remove the brackets
this.mapping.push(uuid);
let uri = kProtocolName + "?camera=" + options.camera +
"&width=" + options.width +
"&height=" + options.height +
"&type=video/x-raw-yuv";
// XXX that's no e10s ready, but the camera inputstream itself is not...
Services.prefs.setCharPref("b2g.camera." + kProtocolName + "?" + uuid, uri);
return kProtocolName + "?" + uuid;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "inner-window-destroyed") {
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId == this.innerWindowID) {
Services.obs.removeObserver(this, "inner-window-destroyed");
for (let aId in this.mapping)
Services.prefs.clearUserPref("b2g.camera." + kProtocolName + "?" + aId);
this.mapping = null;
}
}
},
init: function(aWindow) {
let principal = aWindow.document.nodePrincipal;
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
let perm = Services.perms.testExactPermissionFromPrincipal(principal, "content-camera");
//only pages with perm set and chrome pages can use the camera in content
this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
Services.obs.addObserver(this, "inner-window-destroyed", false);
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
this.innerWindowID = util.currentInnerWindowID;
},
classID: Components.ID("{eff4231b-abce-4f7f-a40a-d646e8fde3ce}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIB2GCameraContent, Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIObserver]),
classInfo: XPCOMUtils.generateCI({classID: Components.ID("{eff4231b-abce-4f7f-a40a-d646e8fde3ce}"),
contractID: "@mozilla.org/b2g-camera-content;1",
interfaces: [Ci.nsIB2GCameraContent],
flags: Ci.nsIClassInfo.DOM_OBJECT,
classDescription: "B2G Camera Content Helper"})
}
const NSGetFactory = XPCOMUtils.generateNSGetFactory([CameraContent]);

View File

@ -9,6 +9,7 @@ const Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Webapps.jsm");
function ContentPermissionPrompt() {}
@ -52,13 +53,29 @@ ContentPermissionPrompt.prototype = {
request.cancel();
});
let principal = request.principal;
let isApp = principal.appStatus != Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED;
let details = {
"type": "permission-prompt",
"permission": request.type,
"id": requestId,
"url": request.principal.URI.spec
type: "permission-prompt",
permission: request.type,
id: requestId,
origin: principal.origin,
isApp: isApp
};
browser.shell.sendChromeEvent(details);
if (!isApp) {
browser.shell.sendChromeEvent(details);
return;
}
// When it's an app, get the manifest to add the l10n application name.
let app = DOMApplicationRegistry.getAppByLocalId(principal.appId);
DOMApplicationRegistry.getManifestFor(app.origin, function getManifest(aManifest) {
let helper = new DOMApplicationManifest(aManifest, app.origin);
details.appName = helper.name;
browser.shell.sendChromeEvent(details);
});
},
classID: Components.ID("{8c719f03-afe0-4aac-91ff-6c215895d467}"),

View File

@ -20,13 +20,13 @@ EXTRA_PP_COMPONENTS = \
ActivitiesGlue.js \
AlertsService.js \
B2GComponents.manifest \
CameraContent.js \
ContentHandler.js \
ContentPermissionPrompt.js \
DirectoryProvider.js \
MozKeyboard.js \
ProcessGlobal.js \
PaymentGlue.js \
YoutubeProtocolHandler.js \
$(NULL)
ifdef MOZ_UPDATER

View File

@ -18,12 +18,28 @@ let log =
function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
function log_noop(msg) { };
const APPLY_PROMPT_TIMEOUT =
Services.prefs.getIntPref("b2g.update.apply-prompt-timeout");
const APPLY_WAIT_TIMEOUT =
Services.prefs.getIntPref("b2g.update.apply-wait-timeout");
const SELF_DESTRUCT_TIMEOUT =
Services.prefs.getIntPref("b2g.update.self-destruct-timeout");
XPCOMUtils.defineLazyServiceGetter(Services, "aus",
"@mozilla.org/updates/update-service;1",
"nsIApplicationUpdateService");
function UpdatePrompt() { }
UpdatePrompt.prototype = {
classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt]),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt,
Ci.nsIRequestObserver,
Ci.nsIProgressEventSink]),
_update: null,
_applyPromptTimer: null,
_applyWaitTimer: null,
_selfDestructTimer: null,
// nsIUpdatePrompt
@ -32,20 +48,146 @@ UpdatePrompt.prototype = {
// updates when on a billed pipe. Initially, opt-in for 3g, but
// that doesn't cover all cases.
checkForUpdates: function UP_checkForUpdates() { },
showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) { },
showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) {
if (!this.sendUpdateEvent("update-available", aUpdate,
this.handleAvailableResult)) {
log("Unable to prompt for available update, forcing download");
this.downloadUpdate(aUpdate);
}
},
showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
// FIXME/bug 737598: we should let the user request that the
// update be applied later, e.g. if they're in the middle of a
// phone call ;).
if (!this.sendUpdateEvent("update-downloaded", aUpdate,
this.handleDownloadedResult)) {
log("Unable to prompt, forcing restart");
this.restartProcess();
return;
}
// Schedule a fallback timeout in case the UI is unable to respond or show
// a prompt for some reason
this._applyPromptTimer = this.createTimer(APPLY_PROMPT_TIMEOUT);
},
showUpdateError: function UP_showUpdateError(aUpdate) {
if (aUpdate.state == "failed") {
log("Failed to download update, errorCode: " + aUpdate.errorCode);
}
},
showUpdateHistory: function UP_showUpdateHistory(aParent) { },
showUpdateInstalled: function UP_showUpdateInstalled() { },
sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate, aCallback) {
let detail = {
displayVersion: aUpdate.displayVersion,
detailsURL: aUpdate.detailsURL
};
let patch = aUpdate.selectedPatch;
if (!patch) {
// For now we just check the first patch to get size information if a
// patch hasn't been selected yet.
if (aUpdate.patchCount == 0) {
log("Warning: no patches available in update");
return false;
}
patch = aUpdate.getPatchAt(0);
}
detail.size = patch.size;
detail.updateType = patch.type;
this._update = aUpdate;
return this.sendChromeEvent(aType, detail, aCallback);
},
sendChromeEvent: function UP_sendChromeEvent(aType, aDetail, aCallback) {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
if (!browser) {
log("Warning: Couldn't send update event " + aType +
": no content browser");
return false;
}
let content = browser.getContentWindow();
if (!content) {
log("Warning: Couldn't send update event " + aType +
": no content window");
return false;
}
let detail = aDetail || {};
detail.type = aType;
if (!aCallback) {
browser.shell.sendChromeEvent(detail);
return true;
}
let resultType = aType + "-result";
let handleContentEvent = (function(e) {
if (!e.detail) {
return;
}
let detail = e.detail;
if (detail.type == resultType) {
aCallback.call(this, detail);
content.removeEventListener("mozContentEvent", handleContentEvent);
this._update = null;
}
}).bind(this);
content.addEventListener("mozContentEvent", handleContentEvent);
browser.shell.sendChromeEvent(detail);
return true;
},
handleAvailableResult: function UP_handleAvailableResult(aDetail) {
// If the user doesn't choose "download", the updater will implicitly call
// showUpdateAvailable again after a certain period of time
switch (aDetail.result) {
case "download":
this.downloadUpdate(this._update);
break;
}
},
handleDownloadedResult: function UP_handleDownloadedResult(aDetail) {
if (this._applyPromptTimer) {
this._applyPromptTimer.cancel();
this._applyPromptTimer = null;
}
switch (aDetail.result) {
case "wait":
// Wait for a fixed period of time, allowing the user to temporarily
// postpone applying an update
this._applyWaitTimer = this.createTimer(APPLY_WAIT_TIMEOUT);
break;
case "restart":
this.restartProcess();
break;
}
},
downloadUpdate: function UP_downloadUpdate(aUpdate) {
Services.aus.downloadUpdate(aUpdate, true);
Services.aus.addDownloadListener(this);
},
restartProcess: function UP_restartProcess() {
log("Update downloaded, restarting to apply it");
// If not cleanly shut down within 5 seconds, this process will
// explode.
this._setSelfDestructTimer(5000);
this._selfDestructTimer = this.createTimer(SELF_DESTRUCT_TIMEOUT);
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup);
// NB: on Gonk, we rely on the system process manager to restart
// us. Trying to restart here would conflict with the process
// manager. We should be using a runtime check to detect Gonk
@ -57,33 +199,60 @@ UpdatePrompt.prototype = {
);
},
_setSelfDestructTimer: function UP__setSelfDestructTimer(timeoutMs) {
notify: function UP_notify(aTimer) {
if (aTimer == this._selfDestructTimer) {
this._selfDestructTimer = null;
this.selfDestruct();
} else if (aTimer == this._applyPromptTimer) {
log("Timed out waiting for result, restarting");
this._applyPromptTimer = null;
this.restartProcess();
} else if (aTimer == this._applyWaitTimer) {
this._applyWaitTimer = null;
this.showUpdatePrompt();
}
},
selfDestruct: function UP_selfDestruct() {
#ifdef ANDROID
Cu.import("resource://gre/modules/ctypes.jsm");
let libc = ctypes.open("libc.so");
let _exit = libc.declare("_exit", ctypes.default_abi,
let _exit = libc.declare("_exit", ctypes.default_abi,
ctypes.void_t, // [return]
ctypes.int); // status
this.notify = function UP_notify(_) {
log("Self-destruct timer fired; didn't cleanly shut down. BOOM");
_exit(0);
}
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(this, timeoutMs, timer.TYPE_ONE_SHOT);
this._selfDestructTimer = timer;
log("Self-destruct timer fired; didn't cleanly shut down. BOOM");
_exit(0);
#endif
},
showUpdateInstalled: function UP_showUpdateInstalled() { },
showUpdateError: function UP_showUpdateError(aUpdate) {
if (aUpdate.state == "failed") {
log("Failed to download update, errorCode: " + aUpdate.errorCode);
}
createTimer: function UP_createTimer(aTimeoutMs) {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(this, aTimeoutMs, timer.TYPE_ONE_SHOT);
return timer;
},
showUpdateHistory: function UP_showUpdateHistory(aParent) { },
// nsIRequestObserver
onStartRequest: function UP_onStartRequest(aRequest, aContext) {
this.sendChromeEvent("update-downloading");
},
onStopRequest: function UP_onStopRequest(aRequest, aContext, aStatusCode) {
Services.aus.removeDownloadListener(this);
},
// nsIProgressEventSink
onProgress: function UP_onProgress(aRequest, aContext, aProgress,
aProgressMax) {
this.sendChromeEvent("update-progress", {
progress: aProgress,
total: aProgressMax
});
},
onStatus: function UP_onStatus(aRequest, aUpdate, aStatus, aStatusArg) { }
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);

View File

@ -0,0 +1,117 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
return Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsIMessageSender);
});
// Splits parameters in a query string.
function extractParameters(aQuery) {
let params = aQuery.split("&");
let res = {};
params.forEach(function(aParam) {
let obj = aParam.split("=");
res[obj[0]] = decodeURIComponent(obj[1]);
});
return res;
}
function YoutubeProtocolHandler() {
}
YoutubeProtocolHandler.prototype = {
classID: Components.ID("{c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
scheme: "vnd.youtube",
defaultPort: -1,
protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
Ci.nsIProtocolHandler.URI_NOAUTH |
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,
// Sample URL:
// vnd.youtube:iNuKL2Gy_QM?vndapp=youtube_mobile&vndclient=mv-google&vndel=watch&vnddnc=1
newURI: function yt_phNewURI(aSpec, aOriginCharset, aBaseURI) {
let uri = Cc["@mozilla.org/network/standard-url;1"]
.createInstance(Ci.nsIStandardURL);
let id = aSpec.substring(this.scheme.length + 1);
id = id.substring(0, id.indexOf('?'));
uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD, -1, this.scheme + "://dummy_host/" + id, aOriginCharset,
aBaseURI);
return uri.QueryInterface(Ci.nsIURI);
},
newChannel: function yt_phNewChannel(aURI) {
// Get a list of streams for this video id.
let infoURI = "http://www.youtube.com/get_video_info?&video_id=" +
aURI.path.substring(1);
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
xhr.open("GET", infoURI, true);
xhr.addEventListener("load", function() {
// Youtube sends the response as a double wrapped url answer:
// we first extract the url_encoded_fmt_stream_map parameter,
// and from each comma-separated entry in this value, we extract
// other parameters (url and type).
let key = "url_encoded_fmt_stream_map=";
let pos = xhr.responseText.indexOf(key);
if (pos == -1) {
return;
}
let streams = decodeURIComponent(xhr.responseText
.substring(pos + key.length)).split(",");
let uri;
let mimeType;
// Recognized mime types, ordered from the less usable to the most usable.
let recognizedTypes = ["video/webm"];
#ifdef MOZ_WIDGET_GONK
recognizedTypes.push("video/mp4");
#endif
let bestType = -1;
streams.forEach(function(aStream) {
let params = extractParameters(aStream);
let url = params["url"];
let type = params["type"] ? params["type"].split(";")[0] : null;
let index;
if (url && type && ((index = recognizedTypes.indexOf(type)) != -1) &&
index > bestType) {
uri = url;
mimeType = type;
bestType = index;
}
});
if (uri && mimeType) {
cpmm.sendAsyncMessage("content-handler", {
url: uri,
type: mimeType
});
}
});
xhr.send(null);
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
},
allowPort: function yt_phAllowPort(aPort, aScheme) {
return false;
}
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([YoutubeProtocolHandler]);

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1346451771000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1347572992000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -96,6 +96,10 @@
<versionRange minVersion="0" maxVersion="15.0.5" severity="1">
</versionRange>
</emItem>
<emItem blockID="i136" id="Adobe@flash.com">
<versionRange minVersion="0" maxVersion="*" severity="1">
</versionRange>
</emItem>
<emItem blockID="i38" id="{B7082FAA-CB62-4872-9106-E42DD88EDE45}">
<versionRange minVersion="0.1" maxVersion="3.3.0.*">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@ -448,6 +452,9 @@
<pluginItem blockID="p129">
<match name="filename" exp="Silverlight\.plugin" /> <versionRange minVersion="0" maxVersion="5.0.99999" severity="1"></versionRange>
</pluginItem>
<pluginItem blockID="p138">
<match name="filename" exp="JavaAppletPlugin\.plugin" /> <versionRange minVersion="Java 7 Update 01" maxVersion="Java 7 Update 06" severity="0"></versionRange>
</pluginItem>
</pluginItems>
<gfxItems>
@ -460,26 +467,6 @@
</devices>
<feature>DIRECT3D_9_LAYERS</feature> <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus> <driverVersion>8.17.12.5896</driverVersion> <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry>
<gfxBlacklistEntry blockID="g37"> <os>WINNT 5.1</os> <vendor>0x10de</vendor> <feature>DIRECT3D_9_LAYERS</feature> <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus> <driverVersion>7.0.0.0</driverVersion> <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry>
<gfxBlacklistEntry blockID="g81"> <os>WINNT 6.1</os> <vendor>0x1002</vendor> <devices>
<device>0x9802</device>
<device>0x9803</device>
<device>0x9803</device>
<device>0x9804</device>
<device>0x9805</device>
<device>0x9806</device>
<device>0x9807</device>
</devices>
<feature>DIRECT2D</feature> <featureStatus>BLOCKED_DEVICE</featureStatus> <driverVersion>1.0.0.0</driverVersion> <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry>
<gfxBlacklistEntry blockID="g95"> <os>WINNT 6.1</os> <vendor>0x1002</vendor> <devices>
<device>0x9802</device>
<device>0x9803</device>
<device>0x9803</device>
<device>0x9804</device>
<device>0x9805</device>
<device>0x9806</device>
<device>0x9807</device>
</devices>
<feature>DIRECT3D_9_LAYERS</feature> <featureStatus>BLOCKED_DEVICE</featureStatus> <driverVersion>1.0.0.0</driverVersion> <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry>
</gfxItems>

View File

@ -194,7 +194,6 @@ pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla
pref("extensions.update.interval", 86400); // Check for updates to Extensions and
// Themes every day
// Non-symmetric (not shared by extensions) extension-specific [update] preferences
pref("extensions.getMoreThemesURL", "https://addons.mozilla.org/%LOCALE%/firefox/getpersonas");
pref("extensions.dss.enabled", false); // Dynamic Skin Switching
pref("extensions.dss.switchPending", false); // Non-dynamic switch pending after next
// restart.

View File

@ -112,28 +112,38 @@ const DEFAULT_SNIPPETS_URLS = [
const SNIPPETS_UPDATE_INTERVAL_MS = 86400000; // 1 Day.
let gSearchEngine;
document.addEventListener("DOMContentLoaded", function init() {
setupSearchEngine();
loadSnippets();
let gObserver = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
if (mutation.attributeName == "searchEngineURL") {
gObserver.disconnect();
setupSearchEngine();
loadSnippets();
return;
}
}
});
window.addEventListener("load", function () {
// Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
// later and may use asynchronous getters.
window.gObserver.observe(document.documentElement, { attributes: true });
fitToWidth();
});
window.addEventListener("load", fitToWidth);
window.addEventListener("resize", fitToWidth);
function onSearchSubmit(aEvent)
{
let searchTerms = document.getElementById("searchText").value;
if (gSearchEngine && searchTerms.length > 0) {
let searchURL = document.documentElement.getAttribute("searchEngineURL");
if (searchURL && searchTerms.length > 0) {
const SEARCH_TOKENS = {
"_searchTerms_": encodeURIComponent(searchTerms)
}
let url = gSearchEngine.searchUrl;
for (let key in SEARCH_TOKENS) {
url = url.replace(key, SEARCH_TOKENS[key]);
searchURL = searchURL.replace(key, SEARCH_TOKENS[key]);
}
window.location.href = url;
window.location.href = searchURL;
}
aEvent.preventDefault();
@ -142,27 +152,24 @@ function onSearchSubmit(aEvent)
function setupSearchEngine()
{
gSearchEngine = JSON.parse(localStorage["search-engine"]);
if (!gSearchEngine)
let searchEngineName = document.documentElement.getAttribute("searchEngineName");
let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
if (!searchEngineInfo) {
return;
// Look for extended information, like logo and links.
let searchEngineInfo = SEARCH_ENGINES[gSearchEngine.name];
if (searchEngineInfo) {
for (let prop in searchEngineInfo)
gSearchEngine[prop] = searchEngineInfo[prop];
}
// Enqueue additional params if required by the engine definition.
if (gSearchEngine.params)
gSearchEngine.searchUrl += "&" + gSearchEngine.params;
if (searchEngineInfo.params) {
let searchEngineURL = document.documentElement.getAttribute("searchEngineURL");
searchEngineURL += "&" + searchEngineInfo.params;
document.documentElement.setAttribute("searchEngineURL", searchEngineURL);
}
// Add search engine logo.
if (gSearchEngine.image) {
if (searchEngineInfo.image) {
let logoElt = document.getElementById("searchEngineLogo");
logoElt.src = gSearchEngine.image;
logoElt.alt = gSearchEngine.name;
logoElt.src = searchEngineInfo.image;
logoElt.alt = searchEngineInfo.name;
}
// The "autofocus" attribute doesn't focus the form element
@ -180,7 +187,7 @@ function loadSnippets()
{
// Check last snippets update.
let lastUpdate = localStorage["snippets-last-update"];
let updateURL = localStorage["snippets-update-url"];
let updateURL = document.documentElement.getAttribute("snippetsURL");
if (updateURL && (!lastUpdate ||
Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS)) {
// Try to update from network.

View File

@ -326,7 +326,7 @@ let SocialShareButton = {
// whenever we notice the provider has changed - but the concept of
// "provider changed" will only exist once bug 774520 lands.
// get the recommend-prompt info.
let port = Social.provider._getWorkerPort();
let port = Social.provider.getWorkerPort();
if (port) {
port.onmessage = function(evt) {
if (evt.data.topic == "social.user-recommend-prompt-response") {

View File

@ -90,6 +90,9 @@ __defineSetter__("PluralForm", function (val) {
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
"resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
"resource:///modules/AboutHomeUtils.jsm");
#ifdef MOZ_SERVICES_SYNC
XPCOMUtils.defineLazyGetter(this, "Weave", function() {
let tmp = {};
@ -2495,10 +2498,19 @@ function BrowserOnAboutPageLoad(document) {
// the hidden attribute set on the apps button in aboutHome.xhtml
if (getBoolPref("browser.aboutHome.apps", false))
document.getElementById("apps").removeAttribute("hidden");
let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
getService(Components.interfaces.nsISessionStore);
if (!ss.canRestoreLastSession)
document.getElementById("launcher").removeAttribute("session");
// Inject search engine and snippets URL.
let docElt = document.documentElement;
docElt.setAttribute("snippetsURL", AboutHomeUtils.snippetsURL);
docElt.setAttribute("searchEngineName",
AboutHomeUtils.defaultSearchEngine.name);
docElt.setAttribute("searchEngineURL",
AboutHomeUtils.defaultSearchEngine.searchURL);
}
}
@ -5675,13 +5687,11 @@ var OfflineApps = {
// OfflineApps Public Methods
init: function ()
{
Services.obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
Services.obs.addObserver(this, "offline-cache-update-completed", false);
},
uninit: function ()
{
Services.obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
Services.obs.removeObserver(this, "offline-cache-update-completed");
},
@ -5800,10 +5810,6 @@ var OfflineApps = {
}
}
var storageManager = Cc["@mozilla.org/dom/storagemanager;1"].
getService(Ci.nsIDOMStorageManager);
usage += storageManager.getUsage(host);
return usage;
},
@ -5923,19 +5929,7 @@ var OfflineApps = {
// nsIObserver
observe: function (aSubject, aTopic, aState)
{
if (aTopic == "dom-storage-warn-quota-exceeded") {
if (aSubject) {
var uri = makeURI(aSubject.location.href);
if (OfflineApps._checkUsage(uri)) {
var browserWindow =
this._getBrowserWindowForContentWindow(aSubject);
var browser = this._getBrowserForContentWindow(browserWindow,
aSubject);
OfflineApps._warnUsage(browser, uri);
}
}
} else if (aTopic == "offline-cache-update-completed") {
if (aTopic == "offline-cache-update-completed") {
var cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
var uri = cacheUpdate.manifestURI;

View File

@ -268,8 +268,12 @@
</hbox>
</panel>
<panel id="social-notification-panel" class="social-panel"
type="arrow" hidden="true" noautofocus="true">
<panel id="social-notification-panel"
class="social-panel"
type="arrow"
hidden="true"
consumeoutsideclicks="true"
noautofocus="true">
<box id="social-notification-box" flex="1"></box>
</panel>
<panel id="social-flyout-panel"

View File

@ -171,10 +171,10 @@ let gUpdater = {
// Set the site's initial opacity to zero.
site.node.style.opacity = 0;
// Without the setTimeout() the node would just appear instead of fade in.
setTimeout(function () {
gTransformation.showSite(site, function () batch.pop());
}, 0);
// Flush all style changes for the dynamically inserted site to make
// the fade-in transition work.
window.getComputedStyle(site.node).opacity;
gTransformation.showSite(site, function () batch.pop());
});
batch.close();

View File

@ -820,7 +820,7 @@ nsContextMenu.prototype = {
canvas.height = video.videoHeight;
var ctxDraw = canvas.getContext("2d");
ctxDraw.drawImage(video, 0, 0);
saveImageURL(canvas.toDataURL("image/jpeg", ""), name, "SaveImageTitle", true, false, document.documentURIObject);
saveImageURL(canvas.toDataURL("image/jpeg", ""), name, "SaveImageTitle", true, false, document.documentURIObject, this.target.ownerDocument);
},
fullScreenVideo: function () {
@ -1061,12 +1061,12 @@ nsContextMenu.prototype = {
if (this.onCanvas) {
// Bypass cache, since it's a data: URL.
saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
true, false, doc.documentURIObject);
true, false, doc.documentURIObject, doc);
}
else if (this.onImage) {
urlSecurityCheck(this.mediaURL, doc.nodePrincipal);
saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
false, doc.documentURIObject);
false, doc.documentURIObject, doc);
}
else if (this.onVideo || this.onAudio) {
urlSecurityCheck(this.mediaURL, doc.nodePrincipal);

View File

@ -85,8 +85,8 @@ Sanitizer.prototype = {
cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE);
} catch(er) {}
var imageCache = Cc["@mozilla.org/image/cache;1"].
getService(Ci.imgICache);
var imageCache = Cc["@mozilla.org/image/tools;1"].
getService(Ci.imgITools).getImgCacheForDocument(null);
try {
imageCache.clearCache(false); // true=chrome, false=content
} catch(er) {}

View File

@ -685,7 +685,10 @@
if (!(aURI instanceof Ci.nsIURI))
aURI = makeURI(aURI);
this.mFaviconService.setAndFetchFaviconForPage(browser.currentURI,
aURI, false);
aURI, false,
gPrivateBrowsingUI.privateWindow ?
this.mFaviconService.FAVICON_LOAD_PRIVATE :
this.mFaviconService.FAVICON_LOAD_NON_PRIVATE);
}
if ((browser.mIconURL || "") != aTab.getAttribute("image")) {

View File

@ -15,64 +15,17 @@ registerCleanupFunction(function() {
let gTests = [
{
desc: "Check that rejecting cookies does not prevent page from working",
desc: "Check that clearing cookies does not clear storage",
setup: function ()
{
Services.prefs.setIntPref("network.cookies.cookieBehavior", 2);
Cc["@mozilla.org/dom/storagemanager;1"]
.getService(Ci.nsIObserver)
.observe(null, "cookie-changed", "cleared");
},
run: function ()
{
let storage = getStorage();
isnot(storage.getItem("search-engine"), null);
try {
Services.prefs.clearUserPref("network.cookies.cookieBehavior");
} catch (ex) {}
executeSoon(runNextTest);
}
},
{
desc: "Check that asking for cookies does not prevent page from working",
setup: function ()
{
Services.prefs.setIntPref("network.cookie.lifetimePolicy", 1);
},
run: function ()
{
let storage = getStorage();
isnot(storage.getItem("search-engine"), null);
try {
Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
} catch (ex) {}
executeSoon(runNextTest);
}
},
{
desc: "Check that clearing cookies does not prevent page from working",
setup: function ()
{
Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIObserver).
observe(null, "cookie-changed", "cleared");
},
run: function ()
{
let storage = getStorage();
isnot(storage.getItem("search-engine"), null);
executeSoon(runNextTest);
}
},
{
desc: "Check normal status is working",
setup: function ()
{
},
run: function ()
{
let storage = getStorage();
isnot(storage.getItem("search-engine"), null);
isnot(storage.getItem("snippets-last-update"), null);
executeSoon(runNextTest);
}
},
@ -120,14 +73,6 @@ function test()
{
waitForExplicitFinish();
// browser-chrome test harness inits browser specifying an hardcoded page
// and this causes nsIBrowserHandler.defaultArgs to not be evaluated since
// there is a predefined argument.
// About:home localStorage is populated with overridden homepage, that is
// setup in the defaultArgs getter.
// Thus to populate about:home we need to get defaultArgs manually.
Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
// Ensure that by default we don't try to check for remote snippets since that
// could be tricky due to network bustages or slowness.
let storage = getStorage();
@ -148,10 +93,20 @@ function runNextTest()
info(test.desc);
test.setup();
let tab = gBrowser.selectedTab = gBrowser.addTab("about:home");
tab.linkedBrowser.addEventListener("load", function (event) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
// Some part of the page is populated on load, so enqueue on it.
executeSoon(test.run);
tab.linkedBrowser.addEventListener("load", function load(event) {
tab.linkedBrowser.removeEventListener("load", load, true);
let observer = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
if (mutation.attributeName == "searchEngineURL") {
observer.disconnect();
executeSoon(test.run);
return;
}
}
});
let docElt = tab.linkedBrowser.contentDocument.documentElement;
observer.observe(docElt, { attributes: true });
}, true);
}
else {

View File

@ -435,7 +435,7 @@ var gAllTests = [
wh.open();
},
function () {
// Test for offline apps data and cache deletion
// Test for offline cache deletion
// Prepare stuff, we will work with www.example.com
var URL = "http://www.example.com";
@ -454,12 +454,6 @@ var gAllTests = [
pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
// Store some user data to localStorage
var dsm = Cc["@mozilla.org/dom/storagemanager;1"]
.getService(Ci.nsIDOMStorageManager);
var localStorage = dsm.getLocalStorageForPrincipal(principal, URL);
localStorage.setItem("test", "value");
// Store something to the offline cache
const nsICache = Components.interfaces.nsICache;
var cs = Components.classes["@mozilla.org/network/cache-service;1"]
@ -477,9 +471,7 @@ var gAllTests = [
this.checkPrefCheckbox("offlineApps", true);
this.acceptDialog();
// Check all has been deleted (data, cache)
is(localStorage.length, 0, "DOM storage cleared");
// Check if the cache has been deleted
var size = -1;
var visitor = {
visitDevice: function (deviceID, deviceInfo)

View File

@ -24,7 +24,7 @@ function test() {
var tests = {
testOpenCloseChat: function(next) {
let chats = document.getElementById("pinnedchats");
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
@ -43,6 +43,7 @@ var tests = {
iframe.addEventListener("unload", function chatUnload() {
iframe.removeEventListener("unload", chatUnload, true);
ok(true, "got chatbox unload on close");
port.close();
next();
}, true);
chats.selectedChat.close();
@ -60,8 +61,9 @@ var tests = {
testManyChats: function(next) {
// open enough chats to overflow the window, then check
// if the menupopup is visible
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
let width = document.documentElement.boxObject.width;
let numToOpen = (width / 200) + 1;
port.onmessage = function (e) {
@ -81,6 +83,7 @@ var tests = {
chats.selectedChat.close();
}
ok(!chats.selectedChat, "chats are all closed");
port.close();
next();
break;
}
@ -92,8 +95,9 @@ var tests = {
},
testWorkerChatWindow: function(next) {
const chatUrl = "https://example.com/browser/browser/base/content/test/social_chat.html";
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
@ -104,6 +108,7 @@ var tests = {
chats.selectedChat.close();
}
ok(!chats.selectedChat, "chats are all closed");
port.close();
ensureSocialUrlNotRemembered(chatUrl);
next();
break;

View File

@ -20,7 +20,7 @@ function test() {
var tests = {
testOpenCloseFlyout: function(next) {
let panel = document.getElementById("social-flyout-panel");
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
@ -31,6 +31,7 @@ var tests = {
case "got-flyout-visibility":
if (e.data.result == "hidden") {
ok(true, "flyout visibility is 'hidden'");
port.close();
next();
} else if (e.data.result == "shown") {
ok(true, "flyout visibility is 'shown");

View File

@ -19,41 +19,46 @@ function test() {
var tests = {
testSidebarMessage: function(next) {
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
Social.provider.port.onmessage = function (e) {
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
// The sidebar message will always come first, since it loads by default
ok(true, "got sidebar message");
port.close();
next();
break;
}
};
},
testIsVisible: function(next) {
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-isVisible-response":
is(e.data.result, true, "Sidebar should be visible by default");
Social.toggleSidebar();
port.close();
next();
}
};
port.postMessage({topic: "test-isVisible"});
},
testIsNotVisible: function(next) {
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-isVisible-response":
is(e.data.result, false, "Sidebar should be hidden");
Services.prefs.clearUserPref("social.sidebar.open");
port.close();
next();
}
};

View File

@ -29,17 +29,23 @@ var tests = {
function triggerIconPanel() {
let statusIcons = document.getElementById("social-status-iconbox");
ok(!statusIcons.firstChild.hidden, "status icon is visible");
// Click the button to trigger its contentPanel
let panel = document.getElementById("social-notification-panel");
EventUtils.synthesizeMouseAtCenter(statusIcons.firstChild, {});
waitForCondition(function() statusIcons.firstChild && !statusIcons.firstChild.hidden,
function() {
// Click the button to trigger its contentPanel
let panel = document.getElementById("social-notification-panel");
EventUtils.synthesizeMouseAtCenter(statusIcons.firstChild, {});
}, "Status icon didn't become non-hidden");
}
let port = Social.provider.port;
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
iconsReady = true;
checkNext();
break;
case "got-panel-message":
ok(true, "got panel message");
// Check the panel isn't in our history.
@ -52,6 +58,7 @@ var tests = {
panel.hidePopup();
} else if (e.data.result == "hidden") {
ok(true, "panel hidden");
port.close();
next();
}
break;
@ -66,24 +73,5 @@ var tests = {
}
}
port.postMessage({topic: "test-init"});
// Our worker sets up ambient notification at the same time as it responds to
// the workerAPI initialization. If it's already initialized, we can
// immediately check the icons, otherwise wait for initialization by
// observing the topic sent out by the social service.
if (Social.provider.workerAPI.initialized) {
iconsReady = true;
checkNext();
} else {
Services.obs.addObserver(function obs() {
Services.obs.removeObserver(obs, "social:ambient-notification-changed");
// Let the other observers (like the one that updates the UI) run before
// checking the icons.
executeSoon(function () {
iconsReady = true;
checkNext();
});
}, "social:ambient-notification-changed", false);
}
}
}

View File

@ -40,7 +40,9 @@ function tabLoaded() {
function testInitial(finishcb) {
ok(Social.provider, "Social provider is active");
ok(Social.provider.enabled, "Social provider is enabled");
ok(Social.provider.port, "Social provider has a port to its FrameWorker");
let port = Social.provider.getWorkerPort();
ok(port, "Social provider has a port to its FrameWorker");
port.close();
let {shareButton, sharePopup} = SocialShareButton;
ok(shareButton, "share button exists");
@ -186,11 +188,74 @@ function testCloseBySpace() {
sharePopup.addEventListener("popuphidden", function listener() {
sharePopup.removeEventListener("popuphidden", listener);
ok(true, "space closed the share popup");
executeSoon(testDisable);
executeSoon(testStillSharedIn2Tabs);
});
EventUtils.synthesizeKey("VK_SPACE", {});
}
function testStillSharedIn2Tabs() {
let toShare = "http://example.com";
let {shareButton} = SocialShareButton;
let initialTab = gBrowser.selectedTab;
if (shareButton.hasAttribute("shared")) {
SocialShareButton.unsharePage();
}
is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' for the initial tab");
let tab1 = gBrowser.selectedTab = gBrowser.addTab(toShare);
let tab1b = gBrowser.getBrowserForTab(tab1);
tab1b.addEventListener("load", function tabLoad(event) {
tab1b.removeEventListener("load", tabLoad, true);
let tab2 = gBrowser.selectedTab = gBrowser.addTab(toShare);
let tab2b = gBrowser.getBrowserForTab(tab2);
tab2b.addEventListener("load", function tabLoad(event) {
tab2b.removeEventListener("load", tabLoad, true);
// should start without either page being shared.
is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' before we've done anything");
EventUtils.synthesizeMouseAtCenter(shareButton, {});
is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
// and switching to the first tab (with the same URL) should still reflect shared.
gBrowser.selectedTab = tab1;
is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
// but switching back the initial one should reflect not shared.
gBrowser.selectedTab = initialTab;
is(shareButton.hasAttribute("shared"), false, "Initial tab should not reflect shared");
gBrowser.selectedTab = tab1;
SocialShareButton.unsharePage();
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
executeSoon(testStillSharedAfterReopen);
}, true);
}, true);
}
function testStillSharedAfterReopen() {
let toShare = "http://example.com";
let {shareButton} = SocialShareButton;
is(shareButton.hasAttribute("shared"), false, "Reopen: Share button should not have 'shared' for the initial tab");
let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
let tabb = gBrowser.getBrowserForTab(tab);
tabb.addEventListener("load", function tabLoad(event) {
tabb.removeEventListener("load", tabLoad, true);
SocialShareButton.sharePage();
is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
gBrowser.removeTab(tab);
// should be on the initial unshared tab now.
is(shareButton.hasAttribute("shared"), false, "Initial tab should be selected and be unshared.");
// now open the same URL - should be back to shared.
tab = gBrowser.selectedTab = gBrowser.addTab(toShare, {skipAnimation: true});
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
tab.linkedBrowser.removeEventListener("load", tabLoad, true);
is(shareButton.hasAttribute("shared"), true, "New tab to previously shared URL should reflect shared");
SocialShareButton.unsharePage();
gBrowser.removeTab(tab);
executeSoon(testDisable);
}, true);
}, true);
}
function testDisable() {
let shareButton = SocialShareButton.shareButton;
Services.prefs.setBoolPref(prefName, false);

View File

@ -11,6 +11,7 @@ onconnect = function(e) {
switch (topic) {
case "test-init":
testPort = port;
port.postMessage({topic: "test-init-done"});
break;
case "sidebar-message":
sidebarPort = port;
@ -69,7 +70,6 @@ onconnect = function(e) {
case "social.initialize":
// This is the workerAPI port, respond and set up a notification icon.
apiPort = port;
port.postMessage({topic: "social.initialize-response"});
let profile = {
portrait: "https://example.com/portrait.jpg",
userName: "trickster",

View File

@ -1615,6 +1615,12 @@
return "passwords";
if (type == "editBookmarkPanel")
return "bookmarks";
if (type == "addon-install-complete") {
if (!Services.prefs.prefHasUserValue("services.sync.username"))
return "addons";
if (!Services.prefs.getBoolPref("services.sync.engine.addons"))
return "addons-sync-disabled";
}
return null;
]]></getter>
</property>
@ -1626,6 +1632,14 @@
);
]]></getter>
</property>
<property name="_notificationLink">
<getter><![CDATA[
if (this._notificationType == "addons-sync-disabled") {
return "https://support.mozilla.org/kb/how-do-i-enable-add-sync";
}
return "https://services.mozilla.com/sync/";
]]></getter>
</property>
<method name="onCloseButtonCommand">
<body><![CDATA[
this._viewsLeft = 0;
@ -1656,7 +1670,8 @@
let viewsLeft = this._viewsLeft;
if (viewsLeft) {
if (Services.prefs.prefHasUserValue("services.sync.username")) {
if (Services.prefs.prefHasUserValue("services.sync.username") &&
this._notificationType != "addons-sync-disabled") {
// If the user has already setup Sync, don't show the notification.
this._viewsLeft = 0;
// Be sure to hide the panel, in case it was visible and the user
@ -1669,7 +1684,7 @@
this._viewsLeft = viewsLeft - 1;
}
this._promolink.setAttribute("href", "https://services.mozilla.com/sync/");
this._promolink.setAttribute("href", this._notificationLink);
this._promolink.value = gNavigatorBundle.getString("syncPromoNotification.learnMoreLinkText");
this.hidden = false;

View File

@ -1328,7 +1328,14 @@ FeedWriter.prototype = {
}
var faviconURI = makeURI(readerURI.prePath + "/favicon.ico");
var self = this;
var usePrivateBrowsing = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsILoadContext)
.usePrivateBrowsing;
this._faviconService.setAndFetchFaviconForPage(readerURI, faviconURI, false,
usePrivateBrowsing ? this._faviconService.FAVICON_LOAD_PRIVATE
: this._faviconService.FAVICON_LOAD_NON_PRIVATE,
function (aURI, aDataLen, aData, aMimeType) {
if (aDataLen > 0) {
var dataURL = "data:" + aMimeType + ";base64," +

View File

@ -577,10 +577,6 @@ nsBrowserContentHandler.prototype = {
} catch (ex) {}
let override = needHomepageOverride(prefb);
if (override != OVERRIDE_NONE) {
// Setup the default search engine to about:home page.
AboutHomeUtils.loadDefaultSearchEngine();
AboutHomeUtils.loadSnippetsURL();
switch (override) {
case OVERRIDE_NEW_PROFILE:
// New profile.
@ -603,13 +599,6 @@ nsBrowserContentHandler.prototype = {
break;
}
}
else {
// No need to override homepage, but update snippets url if the pref has
// been manually changed.
if (Services.prefs.prefHasUserValue(AboutHomeUtils.SNIPPETS_URL_PREF)) {
AboutHomeUtils.loadSnippetsURL();
}
}
} catch (ex) {}
// formatURLPref might return "about:blank" if getting the pref fails
@ -835,41 +824,5 @@ nsDefaultCommandLineHandler.prototype = {
helpInfo : "",
};
let AboutHomeUtils = {
SNIPPETS_URL_PREF: "browser.aboutHomeSnippets.updateUrl",
get _storage() {
let aboutHomeURI = Services.io.newURI("moz-safe-about:home", null, null);
let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
getService(Components.interfaces.nsIScriptSecurityManager).
getNoAppCodebasePrincipal(aboutHomeURI);
let dsm = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager);
return dsm.getLocalStorageForPrincipal(principal, "");
},
loadDefaultSearchEngine: function AHU_loadDefaultSearchEngine()
{
let defaultEngine = Services.search.originalDefaultEngine;
let submission = defaultEngine.getSubmission("_searchTerms_");
if (submission.postData)
throw new Error("Home page does not support POST search engines.");
let engine = {
name: defaultEngine.name
, searchUrl: submission.uri.spec
}
this._storage.setItem("search-engine", JSON.stringify(engine));
},
loadSnippetsURL: function AHU_loadSnippetsURL()
{
const STARTPAGE_VERSION = 3;
let updateURL = Services.prefs
.getCharPref(this.SNIPPETS_URL_PREF)
.replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
updateURL = Services.urlFormatter.formatURL(updateURL);
this._storage.setItem("snippets-update-url", updateURL);
},
};
var components = [nsBrowserContentHandler, nsDefaultCommandLineHandler];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View File

@ -316,10 +316,6 @@ var gAdvancedPane = {
}
}
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager);
usage += storageManager.getUsage(host);
return usage;
},
@ -407,12 +403,6 @@ var gAdvancedPane = {
}
}
// send out an offline-app-removed signal. The nsDOMStorage
// service will clear DOM storage for this host.
var obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
obs.notifyObservers(null, "offline-app-removed", host);
// remove the permission
var pm = Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager);

View File

@ -308,10 +308,6 @@ var gAdvancedPane = {
}
}
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager);
usage += storageManager.getUsage(host);
return usage;
},
@ -399,12 +395,6 @@ var gAdvancedPane = {
}
}
// send out an offline-app-removed signal. The nsDOMStorage
// service will clear DOM storage for this host.
var obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
obs.notifyObservers(null, "offline-app-removed", host);
// remove the permission
var pm = Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager);

View File

@ -651,8 +651,8 @@ PrivateBrowsingService.prototype = {
}
// Image Cache
let (imageCache = Cc["@mozilla.org/image/cache;1"].
getService(Ci.imgICache)) {
let (imageCache = Cc["@mozilla.org/image/tools;1"].
getService(Ci.imgITools).getImgCacheForDocument(null)) {
try {
imageCache.clearCache(false); // true=chrome, false=content
} catch (ex) {

View File

@ -3,15 +3,13 @@
<!DOCTYPE html>
<title>Test for bug 463205</title>
<body>
<body onload="onLoad()">
<iframe src="data:text/html,<input%20id='original'>"></iframe>
<iframe src="browser_463205_helper.html"></iframe>
<iframe src="data:text/html,mark1"></iframe>
<script type="application/javascript">
frames[2].addEventListener("DOMContentLoaded", function() {
frames[2].removeEventListener("DOMContentLoaded", arguments.callee, false);
function onLoad() {
if (frames[2].document.location.href == "data:text/html,mark1") {
frames[2].document.location = "data:text/html,mark2";
}
@ -20,6 +18,6 @@
frames[0].document.location = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_463205_helper.html";
}
}, false);
}
</script>
</body>

View File

@ -224,7 +224,7 @@ let PageThumbs = {
let sh = aWindow.innerHeight;
let {width: thumbnailWidth, height: thumbnailHeight} = aCanvas;
let scale = Math.max(thumbnailWidth / sw, thumbnailHeight / sh);
let scale = Math.min(Math.max(thumbnailWidth / sw, thumbnailHeight / sh), 1);
let scaledWidth = sw * scale;
let scaledHeight = sh * scale;

View File

@ -9,7 +9,11 @@ ENABLE_MARIONETTE=1
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
mk_add_options MOZ_MAKE_FLAGS=-j1
if test -n "${_PYMAKE}"; then
mk_add_options MOZ_MAKE_FLAGS=-j4
else
mk_add_options MOZ_MAKE_FLAGS=-j1
fi
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

View File

@ -18,7 +18,11 @@ export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_MAKE_FLAGS=-j1
if test -n "${_PYMAKE}"; then
mk_add_options MOZ_MAKE_FLAGS=-j4
else
mk_add_options MOZ_MAKE_FLAGS=-j1
fi
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

View File

@ -4,7 +4,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
let EXPORTED_SYMBOLS = [ ];
let EXPORTED_SYMBOLS = [ "Flags" ];
Cu.import("resource:///modules/devtools/gcli.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -13,6 +13,12 @@ Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
// We need to use an object in which to store any flags because a primitive
// would remain undefined.
let Flags = {
addonsLoaded: false
};
/**
* 'addon' command.
*/
@ -286,5 +292,6 @@ AddonManager.getAllAddons(function addonAsync(aAddons) {
return promise;
}
});
Flags.addonsLoaded = true;
Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
});

View File

@ -12,6 +12,10 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
"resource:///modules/devtools/LayoutHelpers.jsm");
// String used as an indication to generate default file name in the following
// format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"
const FILENAME_DEFAULT_VALUE = " ";
/**
* 'screenshot' command
*/
@ -19,57 +23,86 @@ gcli.addCommand({
name: "screenshot",
description: gcli.lookup("screenshotDesc"),
manual: gcli.lookup("screenshotManual"),
returnType: "string",
returnType: "html",
params: [
{
name: "filename",
type: "string",
defaultValue: FILENAME_DEFAULT_VALUE,
description: gcli.lookup("screenshotFilenameDesc"),
manual: gcli.lookup("screenshotFilenameManual")
},
{
name: "delay",
type: { name: "number", min: 0 },
defaultValue: 0,
description: gcli.lookup("screenshotDelayDesc"),
manual: gcli.lookup("screenshotDelayManual")
},
{
name: "fullpage",
type: "boolean",
description: gcli.lookup("screenshotFullPageDesc"),
manual: gcli.lookup("screenshotFullPageManual")
},
{
name: "selector",
type: "node",
defaultValue: null,
description: gcli.lookup("inspectNodeDesc"),
manual: gcli.lookup("inspectNodeManual")
group: gcli.lookup("screenshotGroupOptions"),
params: [
{
name: "clipboard",
type: "boolean",
description: gcli.lookup("screenshotClipboardDesc"),
manual: gcli.lookup("screenshotClipboardManual")
},
{
name: "chrome",
type: "boolean",
description: gcli.lookup("screenshotChromeDesc"),
manual: gcli.lookup("screenshotChromeManual")
},
{
name: "delay",
type: { name: "number", min: 0 },
defaultValue: 0,
description: gcli.lookup("screenshotDelayDesc"),
manual: gcli.lookup("screenshotDelayManual")
},
{
name: "fullpage",
type: "boolean",
description: gcli.lookup("screenshotFullPageDesc"),
manual: gcli.lookup("screenshotFullPageManual")
},
{
name: "selector",
type: "node",
defaultValue: null,
description: gcli.lookup("inspectNodeDesc"),
manual: gcli.lookup("inspectNodeManual")
}
]
}
],
exec: function Command_screenshot(args, context) {
var document = context.environment.contentDocument;
if (args.chrome && args.selector) {
// Node screenshot with chrome option does not work as inteded
// Refer https://bugzilla.mozilla.org/show_bug.cgi?id=659268#c7
// throwing for now.
throw new Error(gcli.lookup("screenshotSelectorChromeConflict"));
}
var document = args.chrome? context.environment.chromeDocument
: context.environment.contentDocument;
if (args.delay > 0) {
var promise = context.createPromise();
document.defaultView.setTimeout(function Command_screenshotDelay() {
let reply = this.grabScreen(document, args.filename);
let reply = this.grabScreen(document, args.filename, args.clipboard,
args.fullpage);
promise.resolve(reply);
}.bind(this), args.delay * 1000);
return promise;
}
else {
return this.grabScreen(document, args.filename, args.fullpage, args.selector);
return this.grabScreen(document, args.filename, args.clipboard,
args.fullpage, args.selector);
}
},
grabScreen:
function Command_screenshotGrabScreen(document, filename, fullpage, node) {
function Command_screenshotGrabScreen(document, filename, clipboard,
fullpage, node) {
let window = document.defaultView;
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
let left = 0;
let top = 0;
let width;
let height;
let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
if (!fullpage) {
if (!node) {
@ -93,19 +126,70 @@ gcli.addCommand({
let ctx = canvas.getContext("2d");
ctx.drawWindow(window, left, top, width, height, "#fff");
let data = canvas.toDataURL("image/png", "");
try {
if (clipboard) {
let io = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
let channel = io.newChannel(data, null, null);
let input = channel.open();
let imgTools = Cc["@mozilla.org/image/tools;1"]
.getService(Ci.imgITools);
let container = {};
imgTools.decodeImageData(input, channel.contentType, container);
let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
.createInstance(Ci.nsISupportsInterfacePointer);
wrapped.data = container.value;
let trans = Cc["@mozilla.org/widget/transferable;1"]
.createInstance(Ci.nsITransferable);
if ("init" in trans) {
trans.init(null);
}
trans.addDataFlavor(channel.contentType);
trans.setTransferData(channel.contentType, wrapped, -1);
let clipid = Ci.nsIClipboard;
let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
clip.setData(trans, null, clipid.kGlobalClipboard);
div.textContent = gcli.lookup("screenshotCopied");
return div;
}
}
catch (ex) {
div.textContent = gcli.lookup("screenshotErrorCopying");
return div;
}
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
// Create a name for the file if not present
if (filename == FILENAME_DEFAULT_VALUE) {
let date = new Date();
let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
"-" + date.getDate();
dateString = dateString.split("-").map(function(part) {
if (part.length == 1) {
part = "0" + part;
}
return part;
}).join("-");
let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
filename = gcli.lookupFormat("screenshotGeneratedFilename",
[dateString, timeString]) + ".png";
}
// Check there is a .png extension to filename
if (!filename.match(/.png$/i)) {
else if (!filename.match(/.png$/i)) {
filename += ".png";
}
// If the filename is relative, tack it onto the download directory
if (!filename.match(/[\\\/]/)) {
let downloadMgr = Cc["@mozilla.org/download-manager;1"]
.getService(Ci.nsIDownloadManager);
.getService(Ci.nsIDownloadManager);
let tempfile = downloadMgr.userDownloadsDirectory;
tempfile.append(filename);
filename = tempfile.path;
@ -114,21 +198,36 @@ gcli.addCommand({
try {
file.initWithPath(filename);
} catch (ex) {
return "Error saving to " + filename;
div.textContent = gcli.lookup("screenshotErrorSavingToFile") + " " + filename;
return div;
}
let ioService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
.getService(Ci.nsIIOService);
let Persist = Ci.nsIWebBrowserPersist;
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(Persist);
.createInstance(Persist);
persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
let source = ioService.newURI(data, "UTF8", null);
persist.saveURI(source, null, null, null, null, file);
return "Saved to " + filename;
div.textContent = gcli.lookup("screenshotSavedToFile") + " " + filename;
div.addEventListener("click", function openFile() {
div.removeEventListener("click", openFile);
file.reveal();
});
div.style.cursor = "pointer";
let image = document.createElement("div");
let previewHeight = parseInt(256*height/width);
image.setAttribute("style",
"width:256px; height:" + previewHeight + "px;" +
"background-image: url('" + data + "');" +
"background-size: 256px " + previewHeight + "px;" +
"margin: 4px; display: block");
div.appendChild(image);
return div;
}
});
});

View File

@ -24,6 +24,7 @@ MOCHITEST_BROWSER_FILES = \
browser_cmd_pagemod_export.js \
browser_cmd_pref.js \
browser_cmd_restart.js \
browser_cmd_screenshot.js \
browser_cmd_settings.js \
browser_gcli_web.js \
head.js \
@ -33,6 +34,7 @@ MOCHITEST_BROWSER_FILES = \
MOCHITEST_BROWSER_FILES += \
browser_dbg_cmd_break.html \
browser_dbg_cmd.html \
browser_cmd_screenshot.html \
browser_cmd_pagemod_export.html \
browser_cmd_jsb_script.jsi \
$(NULL)

View File

@ -3,6 +3,9 @@
// Tests that the addon commands works as they should
let imported = {};
Components.utils.import("resource:///modules/devtools/CmdAddon.jsm", imported);
function test() {
DeveloperToolbarTest.test("about:blank", [ GAT_test ]);
}
@ -10,6 +13,7 @@ function test() {
function GAT_test() {
var GAT_ready = DeveloperToolbarTest.checkCalled(function() {
Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
info("gcli_addon_commands_ready notification received, running tests");
helpers.setInput('addon list dictionary');
helpers.check({
@ -91,4 +95,14 @@ function GAT_test() {
});
Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false);
if (imported.Flags.addonsLoaded) {
info("The getAllAddons command has already completed and we have missed ");
info("the notification. Let's send the gcli_addon_commands_ready ");
info("notification ourselves.");
Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
} else {
info("gcli_addon_commands_ready notification has not yet been received.");
}
}

View File

@ -0,0 +1,6 @@
<html>
<head></head>
<body>
<img id="testImage" ></img>
</body>
</html>

View File

@ -0,0 +1,148 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that screenshot command works properly
const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
"test/browser_cmd_screenshot.html";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
let tempScope = {};
Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
let FileUtils = tempScope.FileUtils;
function test() {
DeveloperToolbarTest.test(TEST_URI, [ testInput, testCapture ]);
}
function testInput() {
helpers.setInput('screenshot');
helpers.check({
input: 'screenshot',
markup: 'VVVVVVVVVV',
status: 'VALID',
args: {
}
});
helpers.setInput('screenshot abc.png');
helpers.check({
input: 'screenshot abc.png',
markup: 'VVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
filename: { value: "abc.png"},
}
});
helpers.setInput('screenshot --fullpage');
helpers.check({
input: 'screenshot --fullpage',
markup: 'VVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
fullpage: { value: true},
}
});
helpers.setInput('screenshot abc --delay 5');
helpers.check({
input: 'screenshot abc --delay 5',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
filename: { value: "abc"},
delay: { value: "5"},
}
});
helpers.setInput('screenshot --selector img#testImage');
helpers.check({
input: 'screenshot --selector img#testImage',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
selector: { value: content.document.getElementById("testImage")},
}
});
}
function testCapture() {
function checkTemporaryFile() {
// Create a temporary file.
let gFile = FileUtils.getFile("TmpD", ["TestScreenshotFile.png"]);
if (gFile.exists()) {
gFile.remove(false);
return true;
}
else {
return false;
}
}
function clearClipboard() {
let clipid = Ci.nsIClipboard;
let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
clip.emptyClipboard(clipid.kGlobalClipboard);
}
function checkClipboard() {
try {
let clipid = Ci.nsIClipboard;
let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
let trans = Cc["@mozilla.org/widget/transferable;1"]
.createInstance(Ci.nsITransferable);
if ("init" in trans) {
trans.init(null);
}
let io = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
let contentType = io.newChannel("", null, null).contentType;
trans.addDataFlavor(contentType);
clip.getData(trans, clipid.kGlobalClipboard);
let str = new Object();
let strLength = new Object();
trans.getTransferData(contentType, str, strLength);
if (str && strLength > 0) {
clip.emptyClipboard(clipid.kGlobalClipboard);
return true;
}
}
catch (ex) {}
return false;
}
let path = FileUtils.getFile("TmpD", ["TestScreenshotFile.png"]).path;
DeveloperToolbarTest.exec({
typed: "screenshot " + path,
args: {
delay: 0,
filename: "" + path,
fullpage: false,
clipboard: false,
node: null,
chrome: false,
},
outputMatch: new RegExp("^Saved to "),
});
ok(checkTemporaryFile, "Screenshot got created");
clearClipboard();
DeveloperToolbarTest.exec({
typed: "screenshot --fullpage --clipboard",
args: {
delay: 0,
filename: " ",
fullpage: true,
clipboard: true,
node: null,
chrome: false,
},
outputMatch: new RegExp("^Copied to clipboard.$"),
});
ok(checkClipboard, "Screenshot got created and copied");
}

View File

@ -46,8 +46,8 @@ DebuggerUI.prototype = {
tabs.addEventListener("TabSelect", bound_refreshCommand, true);
win.addEventListener("unload", function onClose(aEvent) {
tabs.removeEventListener("TabSelect", bound_refreshCommand, true);
win.removeEventListener("unload", onClose, false);
tabs.removeEventListener("TabSelect", bound_refreshCommand, true);
}, false);
},
@ -245,7 +245,7 @@ DebuggerPane.prototype = {
_initServer: function DP__initServer() {
if (!DebuggerServer.initialized) {
// Always allow connections from nsIPipe transports.
DebuggerServer.init(function () { return true; });
DebuggerServer.init(function() true);
DebuggerServer.addBrowserActors();
}
},

View File

@ -20,6 +20,7 @@ Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
/**
* Controls the debugger view by handling the source scripts, the current
@ -51,6 +52,7 @@ let DebuggerController = {
window.removeEventListener("DOMContentLoaded", this._startupDebugger, true);
DebuggerView.cacheView();
DebuggerView.initializeKeys();
DebuggerView.initializePanes();
DebuggerView.initializeEditor(function() {
DebuggerView.GlobalSearch.initialize();
@ -246,23 +248,6 @@ let DebuggerController = {
}.bind(this));
},
/**
* Returns true if this is a remote debugger instance.
* @return boolean
*/
get _isRemoteDebugger() {
return window._remoteFlag;
},
/**
* Returns true if this is a chrome debugger instance.
* @return boolean
*/
get _isChromeDebugger() {
// Directly accessing window.parent.content may throw in some cases.
return !("content" in window.parent) && !this._isRemoteDebugger;
},
/**
* Attempts to quit the current process if allowed.
*/
@ -295,6 +280,26 @@ let DebuggerController = {
}
};
/**
* Returns true if this is a remote debugger instance.
* @return boolean
*/
XPCOMUtils.defineLazyGetter(DebuggerController, "_isRemoteDebugger", function() {
// We're inside a single top level XUL window, not an iframe container.
return !(window.frameElement instanceof XULElement) &&
!!window._remoteFlag;
});
/**
* Returns true if this is a chrome debugger instance.
* @return boolean
*/
XPCOMUtils.defineLazyGetter(DebuggerController, "_isChromeDebugger", function() {
// We're inside a single top level XUL window, but not a remote debugger.
return !(window.frameElement instanceof XULElement) &&
!window._remoteFlag;
});
/**
* ThreadState keeps the UI up to date with the state of the
* thread (paused/attached/etc.).
@ -1272,6 +1277,7 @@ SourceScripts.prototype = {
return self._logError(url, aStatus);
}
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
source = self._convertToUnicode(source);
self._onLoadSourceFinished(url, source, null, options);
aStream.close();
});
@ -1296,8 +1302,8 @@ SourceScripts.prototype = {
if (!Components.isSuccessCode(aStatusCode)) {
return self._logError(url, aStatusCode);
}
self._onLoadSourceFinished(
url, chunks.join(""), channel.contentType, options);
let source = self._convertToUnicode(chunks.join(""), channel.contentCharset);
self._onLoadSourceFinished(url, source, channel.contentType, options);
}
};
@ -1307,6 +1313,28 @@ SourceScripts.prototype = {
}
},
/**
* Convert a given string, encoded in a given character set, to unicode.
* @param string aString
* A string.
* @param string aCharset
* A character set.
* @return string
* A unicode string.
*/
_convertToUnicode: function SS__convertToUnicode(aString, aCharset) {
// Decoding primitives.
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
try {
converter.charset = aCharset || "UTF-8";
return converter.ConvertToUnicode(aString);
} catch(e) {
return aString;
}
},
/**
* Called when a script's source has been loaded.
*

View File

@ -33,12 +33,65 @@ let DebuggerView = {
cacheView: function DV_cacheView() {
this._onTogglePanesButtonPressed = this._onTogglePanesButtonPressed.bind(this);
// Panes and view containers
this._togglePanesButton = document.getElementById("toggle-panes");
this._stackframesAndBreakpoints = document.getElementById("stackframes+breakpoints");
this._stackframes = document.getElementById("stackframes");
this._breakpoints = document.getElementById("breakpoints");
this._variables = document.getElementById("variables");
this._scripts = document.getElementById("scripts");
this._globalSearch = document.getElementById("globalsearch");
this._globalSearchSplitter = document.getElementById("globalsearch-splitter");
// Keys
this._fileSearchKey = document.getElementById("fileSearchKey");
this._lineSearchKey = document.getElementById("lineSearchKey");
this._tokenSearchKey = document.getElementById("tokenSearchKey");
this._globalSearchKey = document.getElementById("globalSearchKey");
this._resumeKey = document.getElementById("resumeKey");
this._stepOverKey = document.getElementById("stepOverKey");
this._stepInKey = document.getElementById("stepInKey");
this._stepOutKey = document.getElementById("stepOutKey");
// Buttons, textboxes etc.
this._resumeButton = document.getElementById("resume");
this._stepOverButton = document.getElementById("step-over");
this._stepInButton = document.getElementById("step-in");
this._stepOutButton = document.getElementById("step-out");
this._scriptsSearchbox = document.getElementById("scripts-search");
this._globalOperatorLabel = document.getElementById("global-operator-label");
this._globalOperatorButton = document.getElementById("global-operator-button");
this._tokenOperatorLabel = document.getElementById("token-operator-label");
this._tokenOperatorButton = document.getElementById("token-operator-button");
this._lineOperatorLabel = document.getElementById("line-operator-label");
this._lineOperatorButton = document.getElementById("line-operator-button");
},
/**
* Applies the correct key labels and tooltips across global view elements.
*/
initializeKeys: function DV_initializeKeys() {
this._resumeButton.setAttribute("tooltiptext",
L10N.getFormatStr("pauseButtonTooltip", [LayoutHelpers.prettyKey(this._resumeKey)]));
this._stepOverButton.setAttribute("tooltiptext",
L10N.getFormatStr("stepOverTooltip", [LayoutHelpers.prettyKey(this._stepOverKey)]));
this._stepInButton.setAttribute("tooltiptext",
L10N.getFormatStr("stepInTooltip", [LayoutHelpers.prettyKey(this._stepInKey)]));
this._stepOutButton.setAttribute("tooltiptext",
L10N.getFormatStr("stepOutTooltip", [LayoutHelpers.prettyKey(this._stepOutKey)]));
this._scriptsSearchbox.setAttribute("placeholder",
L10N.getFormatStr("emptyFilterText", [LayoutHelpers.prettyKey(this._fileSearchKey)]));
this._globalOperatorLabel.setAttribute("value",
L10N.getFormatStr("searchPanelGlobal", [LayoutHelpers.prettyKey(this._globalSearchKey)]));
this._tokenOperatorLabel.setAttribute("value",
L10N.getFormatStr("searchPanelToken", [LayoutHelpers.prettyKey(this._tokenSearchKey)]));
this._lineOperatorLabel.setAttribute("value",
L10N.getFormatStr("searchPanelLine", [LayoutHelpers.prettyKey(this._lineSearchKey)]));
this._globalOperatorButton.setAttribute("label", SEARCH_GLOBAL_FLAG);
this._tokenOperatorButton.setAttribute("label", SEARCH_TOKEN_FLAG);
this._lineOperatorButton.setAttribute("label", SEARCH_LINE_FLAG);
},
/**
@ -92,6 +145,11 @@ let DebuggerView = {
this._stackframesAndBreakpoints.parentNode.removeChild(this._stackframesAndBreakpoints);
this._variables.parentNode.removeChild(this._variables);
this._globalSearch.parentNode.removeChild(this._globalSearch);
// Delete all the cached global view elements.
for (let i in this) {
if (!(this[i] instanceof Function)) delete this[i];
}
},
/**
@ -145,10 +203,12 @@ let DebuggerView = {
if (aVisibleFlag) {
this._stackframesAndBreakpoints.style.marginLeft = "0";
this._togglePanesButton.removeAttribute("stackframesAndBreakpointsHidden");
this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes"));
} else {
let margin = parseInt(this._stackframesAndBreakpoints.getAttribute("width")) + 1;
this._stackframesAndBreakpoints.style.marginLeft = -margin + "px";
this._togglePanesButton.setAttribute("stackframesAndBreakpointsHidden", "true");
this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes"));
}
Prefs.stackframesPaneVisible = aVisibleFlag;
},
@ -168,10 +228,12 @@ let DebuggerView = {
if (aVisibleFlag) {
this._variables.style.marginRight = "0";
this._togglePanesButton.removeAttribute("variablesHidden");
this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes"));
} else {
let margin = parseInt(this._variables.getAttribute("width")) + 1;
this._variables.style.marginRight = -margin + "px";
this._togglePanesButton.setAttribute("variablesHidden", "true");
this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes"));
}
Prefs.variablesPaneVisible = aVisibleFlag;
},
@ -184,7 +246,28 @@ let DebuggerView = {
_stackframes: null,
_breakpoints: null,
_variables: null,
_globalSearch: null
_scripts: null,
_globalSearch: null,
_globalSearchSplitter: null,
_fileSearchKey: null,
_lineSearchKey: null,
_tokenSearchKey: null,
_globalSearchKey: null,
_resumeKey: null,
_stepOverKey: null,
_stepInKey: null,
_stepOutKey: null,
_resumeButton: null,
_stepOverButton: null,
_stepInButton: null,
_stepOutButton: null,
_scriptsSearchbox: null,
_globalOperatorLabel: null,
_globalOperatorButton: null,
_tokenOperatorLabel: null,
_tokenOperatorButton: null,
_lineOperatorLabel: null,
_lineOperatorButton: null
};
/**
@ -245,6 +328,7 @@ function GlobalSearchView() {
this._onLineClick = this._onLineClick.bind(this);
this._onMatchClick = this._onMatchClick.bind(this);
this._onResultsScroll = this._onResultsScroll.bind(this);
this._onFocusLost = this._onFocusLost.bind(this);
this._startSearch = this._startSearch.bind(this);
}
@ -259,6 +343,12 @@ GlobalSearchView.prototype = {
this._splitter.hidden = value;
},
/**
* True if the search results container is hidden.
* @return boolean
*/
get hidden() this._pane.hidden,
/**
* Removes all elements from the search results container, leaving it empty.
*/
@ -381,7 +471,7 @@ GlobalSearchView.prototype = {
for (let [url, text] of this._scriptSources) {
// Check if the search token is not found anywhere in the script source.
if (text.toLowerCase().indexOf(lowerCaseToken) === -1) {
if (!text.toLowerCase().contains(lowerCaseToken)) {
continue;
}
let lines = text.split("\n");
@ -395,7 +485,7 @@ GlobalSearchView.prototype = {
let lowerCaseLine = line.toLowerCase();
// Search is not case sensitive, and is tied to each line in the source.
if (lowerCaseLine.indexOf(lowerCaseToken) === -1) {
if (!lowerCaseLine.contains(lowerCaseToken)) {
continue;
}
@ -675,6 +765,20 @@ GlobalSearchView.prototype = {
this._onMatchClick({ target: matches[this._currentlyFocusedMatch] });
},
/**
* Focuses the previously found match in the source editor.
*/
focusPrevMatch: function DVGS_focusPrevMatch() {
let matches = this._pane.querySelectorAll(".string[match=true]");
if (!matches.length) {
return;
}
if (--this._currentlyFocusedMatch < 0) {
this._currentlyFocusedMatch = matches.length - 1;
}
this._onMatchClick({ target: matches[this._currentlyFocusedMatch] });
},
/**
* Called when a line in the search results container is clicked.
*/
@ -708,6 +812,13 @@ GlobalSearchView.prototype = {
editor.setSelection(offset + range.start, offset + range.start + range.length);
},
/**
* Listener handling the searchbox blur event.
*/
_onFocusLost: function DVGS__onFocusLost(e) {
this.hideAndEmpty();
},
/**
* Listener handling the global search container scroll event.
*/
@ -790,15 +901,18 @@ GlobalSearchView.prototype = {
*/
_pane: null,
_splitter: null,
_searchbox: null,
/**
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVS_initialize() {
this._pane = document.getElementById("globalsearch");
this._splitter = document.getElementById("globalsearch-splitter");
initialize: function DVGS_initialize() {
this._pane = DebuggerView._globalSearch;
this._splitter = DebuggerView._globalSearchSplitter;
this._searchbox = DebuggerView._scriptsSearchbox;
this._pane.addEventListener("scroll", this._onResultsScroll, false);
this._searchbox.addEventListener("blur", this._onFocusLost, false);
},
/**
@ -806,10 +920,12 @@ GlobalSearchView.prototype = {
*/
destroy: function DVS_destroy() {
this._pane.removeEventListener("scroll", this._onResultsScroll, false);
this._searchbox.removeEventListener("blur", this._onFocusLost, false);
this.hideAndEmpty();
this._pane = null;
this._splitter = null;
this._searchbox = null;
this._scriptSources = null;
}
};
@ -819,8 +935,10 @@ GlobalSearchView.prototype = {
*/
function ScriptsView() {
this._onScriptsChange = this._onScriptsChange.bind(this);
this._onScriptsSearchClick = this._onScriptsSearchClick.bind(this);
this._onScriptsSearchBlur = this._onScriptsSearchBlur.bind(this);
this._onScriptsSearch = this._onScriptsSearch.bind(this);
this._onScriptsKeyUp = this._onScriptsKeyUp.bind(this);
this._onScriptsKeyPress = this._onScriptsKeyPress.bind(this);
}
ScriptsView.prototype = {
@ -1168,6 +1286,10 @@ ScriptsView.prototype = {
DebuggerController.SourceScripts.showScript(selectedItem.getUserData("sourceScript"));
},
_prevSearchedFile: "",
_prevSearchedLine: 0,
_prevSearchedToken: "",
/**
* Performs a file search if necessary.
*
@ -1254,6 +1376,20 @@ ScriptsView.prototype = {
this._prevSearchedToken = aToken;
},
/**
* The focus listener for the scripts search box.
*/
_onScriptsSearchClick: function DVS__onScriptsSearchClick() {
this._searchboxPanel.openPopup(this._searchbox);
},
/**
* The blur listener for the scripts search box.
*/
_onScriptsSearchBlur: function DVS__onScriptsSearchBlur() {
this._searchboxPanel.hidePopup();
},
/**
* The search listener for the scripts search box.
*/
@ -1262,6 +1398,8 @@ ScriptsView.prototype = {
if (!this._scripts.itemCount) {
return;
}
this._searchboxPanel.hidePopup();
let [file, line, token, isGlobal] = this.searchboxInfo;
// If this is a global script search, schedule a search in all the sources,
@ -1277,28 +1415,45 @@ ScriptsView.prototype = {
},
/**
* The keyup listener for the scripts search box.
* The keypress listener for the scripts search box.
*/
_onScriptsKeyUp: function DVS__onScriptsKeyUp(e) {
_onScriptsKeyPress: function DVS__onScriptsKeyPress(e) {
if (e.keyCode === e.DOM_VK_ESCAPE) {
DebuggerView.editor.focus();
return;
}
var action;
if (e.keyCode === e.DOM_VK_RETURN || e.keyCode === e.DOM_VK_ENTER) {
let token = this.searchboxInfo[2];
let isGlobal = this.searchboxInfo[3];
if (e.keyCode === e.DOM_VK_DOWN ||
e.keyCode === e.DOM_VK_RETURN ||
e.keyCode === e.DOM_VK_ENTER) {
action = 1;
} else if (e.keyCode === e.DOM_VK_UP) {
action = 2;
}
if (!token.length) {
if (action) {
let [file, line, token, isGlobal] = this.searchboxInfo;
if (token.length) {
e.preventDefault();
e.stopPropagation();
} else {
return;
}
if (isGlobal) {
DebuggerView.GlobalSearch.focusNextMatch();
if (DebuggerView.GlobalSearch.hidden) {
DebuggerView.GlobalSearch.scheduleSearch();
} else {
DebuggerView.GlobalSearch[action === 1
? "focusNextMatch"
: "focusPrevMatch"]();
}
return;
}
let editor = DebuggerView.editor;
let offset = editor.findNext(true);
let offset = editor[action === 1 ? "findNext" : "findPrevious"](true);
if (offset > -1) {
editor.setSelection(offset, offset + token.length)
}
@ -1314,11 +1469,20 @@ ScriptsView.prototype = {
DebuggerView.GlobalSearch.hideAndEmpty();
},
/**
* Called when the scripts path filter key sequence was pressed.
*/
_onFileSearch: function DVS__onFileSearch() {
this._onSearch();
this._searchboxPanel.openPopup(this._searchbox);
},
/**
* Called when the scripts token filter key sequence was pressed.
*/
_onLineSearch: function DVS__onLineSearch() {
this._onSearch(SEARCH_LINE_FLAG);
this._searchboxPanel.hidePopup();
},
/**
@ -1326,6 +1490,7 @@ ScriptsView.prototype = {
*/
_onTokenSearch: function DVS__onTokenSearch() {
this._onSearch(SEARCH_TOKEN_FLAG);
this._searchboxPanel.hidePopup();
},
/**
@ -1333,6 +1498,7 @@ ScriptsView.prototype = {
*/
_onGlobalSearch: function DVS__onGlobalSearch() {
this._onSearch(SEARCH_GLOBAL_FLAG);
this._searchboxPanel.hidePopup();
},
/**
@ -1340,17 +1506,22 @@ ScriptsView.prototype = {
*/
_scripts: null,
_searchbox: null,
_searchboxPanel: null,
/**
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVS_initialize() {
this._scripts = document.getElementById("scripts");
this._scripts = DebuggerView._scripts;
this._searchbox = document.getElementById("scripts-search");
this._searchboxPanel = document.getElementById("scripts-search-panel");
this._scripts.addEventListener("select", this._onScriptsChange, false);
this._searchbox.addEventListener("click", this._onScriptsSearchClick, false);
this._searchbox.addEventListener("blur", this._onScriptsSearchBlur, false);
this._searchbox.addEventListener("select", this._onScriptsSearch, false);
this._searchbox.addEventListener("input", this._onScriptsSearch, false);
this._searchbox.addEventListener("keyup", this._onScriptsKeyUp, false);
this._searchbox.addEventListener("keypress", this._onScriptsKeyPress, false);
this.commitScripts();
},
@ -1359,13 +1530,16 @@ ScriptsView.prototype = {
*/
destroy: function DVS_destroy() {
this._scripts.removeEventListener("select", this._onScriptsChange, false);
this._searchbox.removeEventListener("click", this._onScriptsSearchClick, false);
this._searchbox.removeEventListener("blur", this._onScriptsSearchBlur, false);
this._searchbox.removeEventListener("select", this._onScriptsSearch, false);
this._searchbox.removeEventListener("input", this._onScriptsSearch, false);
this._searchbox.removeEventListener("keyup", this._onScriptsKeyUp, false);
this._searchbox.removeEventListener("keypress", this._onScriptsKeyPress, false);
this.empty();
this._scripts = null;
this._searchbox = null;
this._searchboxPanel = null;
}
};
@ -1384,26 +1558,27 @@ function StackFramesView() {
StackFramesView.prototype = {
/**
* Sets the current frames state based on the debugger active thread state.
*
* @param string aState
* Either "paused" or "attached".
*/
updateState: function DVF_updateState(aState) {
let resume = document.getElementById("resume");
/**
* Sets the current frames state based on the debugger active thread state.
*
* @param string aState
* Either "paused" or "attached".
*/
updateState: function DVF_updateState(aState) {
let resume = DebuggerView._resumeButton;
let resumeKey = LayoutHelpers.prettyKey(DebuggerView._resumeKey);
// If we're paused, show a pause label and a resume label on the button.
if (aState == "paused") {
resume.setAttribute("tooltiptext", L10N.getStr("resumeTooltip"));
resume.setAttribute("checked", true);
}
// If we're attached, do the opposite.
else if (aState == "attached") {
resume.setAttribute("tooltiptext", L10N.getStr("pauseTooltip"));
resume.removeAttribute("checked");
}
},
// If we're paused, show a pause label and a resume label on the button.
if (aState == "paused") {
resume.setAttribute("tooltiptext", L10N.getFormatStr("resumeButtonTooltip", [resumeKey]));
resume.setAttribute("checked", true);
}
// If we're attached, do the opposite.
else if (aState == "attached") {
resume.setAttribute("tooltiptext", L10N.getFormatStr("pauseButtonTooltip", [resumeKey]));
resume.removeAttribute("checked");
}
},
/**
* Removes all elements from the stackframes container, leaving it empty.
@ -1638,11 +1813,11 @@ StackFramesView.prototype = {
initialize: function DVF_initialize() {
let close = document.getElementById("close");
let pauseOnExceptions = document.getElementById("pause-exceptions");
let resume = document.getElementById("resume");
let stepOver = document.getElementById("step-over");
let stepIn = document.getElementById("step-in");
let stepOut = document.getElementById("step-out");
let frames = document.getElementById("stackframes");
let resume = DebuggerView._resumeButton;
let stepOver = DebuggerView._stepOverButton;
let stepIn = DebuggerView._stepInButton;
let stepOut = DebuggerView._stepOutButton;
let frames = DebuggerView._stackframes;
close.addEventListener("click", this._onCloseButtonClick, false);
pauseOnExceptions.checked = DebuggerController.StackFrames.pauseOnExceptions;
@ -1665,11 +1840,11 @@ StackFramesView.prototype = {
destroy: function DVF_destroy() {
let close = document.getElementById("close");
let pauseOnExceptions = document.getElementById("pause-exceptions");
let resume = document.getElementById("resume");
let stepOver = document.getElementById("step-over");
let stepIn = document.getElementById("step-in");
let stepOut = document.getElementById("step-out");
let frames = this._frames;
let resume = DebuggerView._resumeButton;
let stepOver = DebuggerView._stepOverButton;
let stepIn = DebuggerView._stepInButton;
let stepOut = DebuggerView._stepOutButton;
let frames = DebuggerView._stackframes;
close.removeEventListener("click", this._onCloseButtonClick, false);
pauseOnExceptions.removeEventListener("click", this._onPauseExceptionsClick, false);
@ -2208,7 +2383,7 @@ BreakpointsView.prototype = {
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVB_initialize() {
let breakpoints = document.getElementById("breakpoints");
let breakpoints = DebuggerView._breakpoints;
breakpoints.addEventListener("click", this._onBreakpointClick, false);
this._breakpoints = breakpoints;
@ -3314,7 +3489,7 @@ PropertiesView.prototype = {
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVP_initialize() {
this._vars = document.getElementById("variables");
this._vars = DebuggerView._variables;
this.emptyText();
this.createHierarchyStore();

View File

@ -37,23 +37,23 @@
<commandset id="sourceEditorCommands"/>
<keyset id="sourceEditorKeys"/>
<keyset id="scriptSearchKeys">
<key key="P" modifiers="access shift"
oncommand="DebuggerView.Scripts._onSearch()"/>
<key key="G" modifiers="access shift"
<key id="fileSearchKey" key="P" modifiers="access shift"
oncommand="DebuggerView.Scripts._onFileSearch()"/>
<key id="lineSearchKey" key="G" modifiers="access shift"
oncommand="DebuggerView.Scripts._onLineSearch()"/>
<key key="T" modifiers="access shift"
<key id="tokenSearchKey" key="T" modifiers="access shift"
oncommand="DebuggerView.Scripts._onTokenSearch()"/>
<key key="F" modifiers="access shift"
<key id="globalSearchKey" key="F" modifiers="access shift"
oncommand="DebuggerView.Scripts._onGlobalSearch()"/>
</keyset>
<keyset id="threadStateKeys">
<key keycode="VK_F6"
<key id="resumeKey" keycode="VK_F6"
oncommand="DebuggerView.StackFrames._onResume()"/>
<key keycode="VK_F7"
<key id="stepOverKey" keycode="VK_F7"
oncommand="DebuggerView.StackFrames._onStepOver()"/>
<key keycode="VK_F8"
<key id="stepInKey" keycode="VK_F8"
oncommand="DebuggerView.StackFrames._onStepIn()"/>
<key keycode="VK_F8" modifiers="shift"
<key id="stepOutKey" keycode="VK_F8" modifiers="shift"
oncommand="DebuggerView.StackFrames._onStepOut()"/>
</keyset>
@ -74,23 +74,19 @@
tabindex="0"/>
<toolbarbutton id="step-over"
class="devtools-toolbarbutton"
tooltiptext="&debuggerUI.stepOverButton.tooltip;"
tabindex="0"/>
<toolbarbutton id="step-in"
class="devtools-toolbarbutton"
tooltiptext="&debuggerUI.stepInButton.tooltip;"
tabindex="0"/>
<toolbarbutton id="step-out"
class="devtools-toolbarbutton"
tooltiptext="&debuggerUI.stepOutButton.tooltip;"
tabindex="0"/>
</hbox>
<menulist id="scripts" class="devtools-menulist"
sizetopopup="always"
label="&debuggerUI.emptyScriptText;"/>
<textbox id="scripts-search" type="search"
class="devtools-searchinput"
emptytext="&debuggerUI.emptyFilterText;"/>
class="devtools-searchinput"/>
<checkbox id="pause-exceptions"
type="checkbox"
tabindex="0"
@ -102,8 +98,31 @@
class="devtools-closebutton"/>
#endif
</toolbar>
<panel id="scripts-search-panel"
type="arrow"
noautofocus="true"
position="before_start">
<vbox>
<label class="description" value="&debuggerUI.searchPanelTitle;"/>
<hbox align="center">
<button id="global-operator-button" class="operator"
onclick="DebuggerView.Scripts._onGlobalSearch()"/>
<label id="global-operator-label" class="plain operator"/>
</hbox>
<hbox align="center">
<button id="token-operator-button" class="operator"
onclick="DebuggerView.Scripts._onTokenSearch()"/>
<label id="token-operator-label" class="plain operator"/>
</hbox>
<hbox align="center">
<button id="line-operator-button" class="operator"
onclick="DebuggerView.Scripts._onLineSearch()"/>
<label id="line-operator-label" class="plain operator"/>
</hbox>
</vbox>
</panel>
<vbox id="dbg-content" flex="1">
<vbox id="globalsearch" hidden="true" flex="1"/>
<vbox id="globalsearch" hidden="true"/>
<splitter id="globalsearch-splitter"
class="devtools-horizontal-splitter" hidden="true"/>
<hbox flex="1">

View File

@ -56,6 +56,8 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_scripts-searching-05.js \
browser_dbg_scripts-searching-06.js \
browser_dbg_scripts-searching-07.js \
browser_dbg_scripts-searching-08.js \
browser_dbg_scripts-searching-popup.js \
browser_dbg_pause-resume.js \
browser_dbg_update-editor-mode.js \
$(warning browser_dbg_select-line.js temporarily disabled due to oranges, see bug 726609) \

View File

@ -70,6 +70,8 @@ function test()
isnot(editor.getText().indexOf("debugger"), -1,
"The correct script was loaded initially.");
isnot(editor.getText().indexOf("\u263a"), -1,
"Unicode characters are converted correctly.");
contextMenu = gDebugger.document.getElementById("sourceEditorContextMenu");
ok(contextMenu, "source editor context menupopup");

View File

@ -16,9 +16,7 @@ function test() {
ok(pane, "toggleDebugger() should return a pane.");
let frame = pane._frame;
frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
wait_for_connect_and_resume(function() {
let cmd = document.getElementById("Tools:Debugger");
is(cmd.getAttribute("checked"), "true", "<command Tools:Debugger> is checked.");
@ -33,10 +31,10 @@ function test() {
let pane = DebuggerUI.toggleDebugger();
is(cmd.getAttribute("checked"), "false", "<command Tools:Debugger> is unchecked once closed.");
}, true);
});
frame.addEventListener("Debugger:Unloaded", function dbgUnloaded() {
frame.removeEventListener("Debugger:Unloaded", dbgUnloaded, true);
window.addEventListener("Debugger:Shutdown", function dbgShutdown() {
window.removeEventListener("Debugger:Shutdown", dbgShutdown, true);
removeTab(tab1);
removeTab(tab2);
@ -45,4 +43,3 @@ function test() {
});
});
}

View File

@ -20,14 +20,11 @@ function test() {
is(DebuggerUI.getDebugger(), pane,
"getDebugger() should return the same pane as toggleDebugger().");
let frame = pane._frame;
let content = pane.contentWindow;
let stackframes;
let variables;
frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
wait_for_connect_and_resume(function() {
ok(content.Prefs.stackframesWidth,
"The debugger preferences should have a saved stackframesWidth value.");
ok(content.Prefs.variablesWidth,
@ -45,11 +42,10 @@ function test() {
variables.setAttribute("width", someWidth2);
removeTab(tab1);
});
}, true);
frame.addEventListener("Debugger:Unloaded", function dbgUnloaded() {
frame.removeEventListener("Debugger:Unloaded", dbgUnloaded, true);
window.addEventListener("Debugger:Shutdown", function dbgShutdown() {
window.removeEventListener("Debugger:Shutdown", dbgShutdown, true);
is(content.Prefs.stackframesWidth, stackframes.getAttribute("width"),
"The stackframes pane width should have been saved by now.");

View File

@ -7,12 +7,18 @@
var gPane = null;
var gTab = null;
var gDebugger = null;
var gView = null;
var gLH = null;
var gL10N = null;
function test() {
debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gPane = aPane;
gDebugger = gPane.contentWindow;
gView = gDebugger.DebuggerView;
gLH = gDebugger.LayoutHelpers;
gL10N = gDebugger.L10N;
testPause();
});
@ -23,7 +29,8 @@ function testPause() {
"Should be running after debug_tab_pane.");
let button = gDebugger.document.getElementById("resume");
is(button.getAttribute("tooltiptext"), gDebugger.L10N.getStr("pauseTooltip"),
is(button.getAttribute("tooltiptext"),
gL10N.getFormatStr("pauseButtonTooltip", [gLH.prettyKey(gView._resumeKey)]),
"Button tooltip should be pause when running.");
gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function() {
@ -35,7 +42,8 @@ function testPause() {
is(gDebugger.DebuggerController.activeThread.paused, true,
"Should be paused after an interrupt request.");
is(button.getAttribute("tooltiptext"), gDebugger.L10N.getStr("resumeTooltip"),
is(button.getAttribute("tooltiptext"),
gL10N.getFormatStr("resumeButtonTooltip", [gLH.prettyKey(gView._resumeKey)]),
"Button tooltip should be resume when paused.");
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
@ -58,7 +66,8 @@ function testResume() {
"Should be paused after an interrupt request.");
let button = gDebugger.document.getElementById("resume");
is(button.getAttribute("tooltiptext"), gDebugger.L10N.getStr("pauseTooltip"),
is(button.getAttribute("tooltiptext"),
gL10N.getFormatStr("pauseButtonTooltip", [gLH.prettyKey(gView._resumeKey)]),
"Button tooltip should be pause when running.");
closeDebuggerAndFinish();
@ -74,4 +83,8 @@ registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebugger = null;
gView = null;
gLH = null;
gL10N = null;
});

View File

@ -11,6 +11,10 @@ var gScripts = null;
var gSearchBox = null;
var gMenulist = null;
/**
* Tests basic functionality of scripts filtering (token search and line jump).
*/
function test()
{
let scriptShown = false;
@ -64,26 +68,31 @@ function testScriptSearching() {
gEditor.getCaretPosition().col == 44 + token.length,
"The editor didn't jump to the correct token. (1)");
EventUtils.sendKey("RETURN");
EventUtils.sendKey("DOWN");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2 + token.length,
"The editor didn't jump to the correct token. (2)");
EventUtils.sendKey("ENTER");
EventUtils.sendKey("DOWN");
ok(gEditor.getCaretPosition().line == 12 &&
gEditor.getCaretPosition().col == 8 + token.length,
"The editor didn't jump to the correct token. (3)");
EventUtils.sendKey("ENTER");
EventUtils.sendKey("RETURN");
ok(gEditor.getCaretPosition().line == 19 &&
gEditor.getCaretPosition().col == 4 + token.length,
"The editor didn't jump to the correct token. (4)");
EventUtils.sendKey("RETURN");
EventUtils.sendKey("ENTER");
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44 + token.length,
"The editor didn't jump to the correct token. (5)");
EventUtils.sendKey("UP");
ok(gEditor.getCaretPosition().line == 19 &&
gEditor.getCaretPosition().col == 4 + token.length,
"The editor didn't jump to the correct token. (5.1)");
token = "debugger;";
write(":bogus#" + token);
@ -152,12 +161,12 @@ function testScriptSearching() {
"The editor didn't jump to the correct token. (14)");
EventUtils.sendKey("RETURN");
EventUtils.sendKey("DOWN");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2 + token.length,
"The editor didn't jump to the correct token. (15)");
EventUtils.sendKey("ENTER");
EventUtils.sendKey("DOWN");
ok(gEditor.getCaretPosition().line == 12 &&
gEditor.getCaretPosition().col == 8 + token.length,
"The editor didn't jump to the correct token. (16)");
@ -172,10 +181,15 @@ function testScriptSearching() {
gEditor.getCaretPosition().col == 44 + token.length,
"The editor didn't jump to the correct token. (18)");
EventUtils.sendKey("UP");
ok(gEditor.getCaretPosition().line == 19 &&
gEditor.getCaretPosition().col == 4 + token.length,
"The editor didn't jump to the correct token. (18.1)");
clear();
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44 + token.length,
ok(gEditor.getCaretPosition().line == 19 &&
gEditor.getCaretPosition().col == 4 + token.length,
"The editor didn't remain at the correct token. (19)");
is(gScripts.visibleItemsCount, 1,
"Not all the scripts are shown after the search. (20)");

View File

@ -4,6 +4,10 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests basic functionality of scripts filtering (file search).
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;

View File

@ -4,6 +4,11 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests basic functionality of global search (lowercase + upper case, expected
* UI behavior, number of results found etc.)
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
@ -322,5 +327,6 @@ registerCleanupFunction(function() {
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchView = null;
gSearchBox = null;
});

View File

@ -4,6 +4,11 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests if the global search results switch back and forth, and wrap around
* when switching between them.
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
@ -124,7 +129,7 @@ function doFirstJump() {
}
});
executeSoon(function() {
EventUtils.sendKey("ENTER");
EventUtils.sendKey("DOWN");
});
}
@ -151,7 +156,7 @@ function doSecondJump() {
}
});
executeSoon(function() {
EventUtils.sendKey("ENTER");
EventUtils.sendKey("RETURN");
});
}
@ -171,7 +176,7 @@ function doWrapAroundJump() {
is(gScripts.visibleItemsCount, 2,
"Not all the correct scripts are shown after the search. (3)");
testSearchTokenEmpty();
doBackwardsWrapAroundJump();
});
} else {
ok(false, "We jumped in a bowl of hot lava (aka WRONG MATCH). That was bad for us.");
@ -182,6 +187,33 @@ function doWrapAroundJump() {
});
}
function doBackwardsWrapAroundJump() {
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
info("Current script url:\n" + aEvent.detail.url + "\n");
info("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
executeSoon(function() {
info("Editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
ok(gEditor.getCaretPosition().line == 5 &&
gEditor.getCaretPosition().col == 6,
"The editor didn't jump to the correct line. (4)");
is(gScripts.visibleItemsCount, 2,
"Not all the correct scripts are shown after the search. (4)");
testSearchTokenEmpty();
});
} else {
ok(false, "We jumped in a bowl of hot lava (aka WRONG MATCH). That was bad for us.");
}
});
executeSoon(function() {
EventUtils.sendKey("UP");
});
}
function testSearchTokenEmpty() {
window.addEventListener("Debugger:GlobalSearch:TokenEmpty", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
@ -189,14 +221,14 @@ function testSearchTokenEmpty() {
info("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = gScripts.selected;
if (url.indexOf("-01.js") != -1) {
if (url.indexOf("-02.js") != -1) {
executeSoon(function() {
info("Editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
ok(gEditor.getCaretPosition().line == 4 &&
ok(gEditor.getCaretPosition().line == 5 &&
gEditor.getCaretPosition().col == 6,
"The editor didn't remain at the correct line. (4)");
"The editor didn't remain at the correct line. (5)");
is(gScripts.visibleItemsCount, 2,
"Not all the correct scripts are shown after the search. (4)");
"Not all the correct scripts are shown after the search. (5)");
is(gSearchView._pane.childNodes.length, 0,
"The global search pane shouldn't have any child nodes after clear().");
@ -247,5 +279,6 @@ registerCleanupFunction(function() {
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchView = null;
gSearchBox = null;
});

View File

@ -4,6 +4,11 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests if the global search results are cleared on location changes, and
* the expected UI behaviors are triggered.
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
@ -166,5 +171,6 @@ registerCleanupFunction(function() {
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchView = null;
gSearchBox = null;
});

View File

@ -4,6 +4,11 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests if the global search results trigger MatchFound and NoMatchFound events
* properly, and triggers the expected UI behavior.
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
@ -139,5 +144,6 @@ registerCleanupFunction(function() {
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchView = null;
gSearchBox = null;
});

View File

@ -4,6 +4,12 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests if the global search results are expanded on scroll or click, and
* clicking matches makes the source editor shows the correct script and
* makes a selection based on the match.
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
@ -243,5 +249,6 @@ registerCleanupFunction(function() {
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchView = null;
gSearchBox = null;
});

View File

@ -0,0 +1,209 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
/**
* Tests if the global search results are hidden when they're supposed to
* (after a focus lost, or when ESCAPE is pressed).
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
var gEditor = null;
var gScripts = null;
var gSearchView = null;
var gSearchBox = null;
function test()
{
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
});
gDebuggee.firstCall();
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: testScriptSearching }, 0);
}
}
}
function testScriptSearching() {
gDebugger.DebuggerController.activeThread.resume(function() {
gEditor = gDebugger.DebuggerView.editor;
gScripts = gDebugger.DebuggerView.Scripts;
gSearchView = gDebugger.DebuggerView.GlobalSearch;
gSearchBox = gScripts._searchbox;
doSearch();
});
}
function doSearch() {
is(gSearchView._pane.hidden, true,
"The global search pane shouldn't be visible yet.");
window.addEventListener("Debugger:GlobalSearch:MatchFound", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
info("Current script url:\n" + gScripts.selected + "\n");
info("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = gScripts.selected;
if (url.indexOf("-02.js") != -1) {
executeSoon(function() {
testFocusLost();
});
} else {
ok(false, "The current script shouldn't have changed after a global search.");
}
});
executeSoon(function() {
write("!a");
});
}
function testFocusLost()
{
is(gSearchView._pane.hidden, false,
"The global search pane should be visible after a search.");
window.addEventListener("Debugger:GlobalSearch:ViewCleared", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
info("Current script url:\n" + gScripts.selected + "\n");
info("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = gScripts.selected;
if (url.indexOf("-02.js") != -1) {
executeSoon(function() {
reshowSearch();
});
} else {
ok(false, "The current script shouldn't have changed after the global search stopped.");
}
});
executeSoon(function() {
gDebugger.DebuggerView.editor.focus();
});
}
function reshowSearch() {
is(gSearchView._pane.hidden, true,
"The global search pane shouldn't be visible after the search was stopped.");
window.addEventListener("Debugger:GlobalSearch:MatchFound", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
info("Current script url:\n" + gScripts.selected + "\n");
info("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = gScripts.selected;
if (url.indexOf("-02.js") != -1) {
executeSoon(function() {
testEscape();
});
} else {
ok(false, "The current script shouldn't have changed after a global re-search.");
}
});
executeSoon(function() {
sendEnter();
});
}
function testEscape()
{
is(gSearchView._pane.hidden, false,
"The global search pane should be visible after a re-search.");
window.addEventListener("Debugger:GlobalSearch:ViewCleared", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
info("Current script url:\n" + gScripts.selected + "\n");
info("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = gScripts.selected;
if (url.indexOf("-02.js") != -1) {
executeSoon(function() {
finalCheck();
});
} else {
ok(false, "The current script shouldn't have changed after the global search escaped.");
}
});
executeSoon(function() {
sendEscape();
});
}
function finalCheck()
{
is(gSearchView._pane.hidden, true,
"The global search pane shouldn't be visible after the search was escaped.");
closeDebuggerAndFinish();
}
function clear() {
gSearchBox.focus();
gSearchBox.value = "";
}
function write(text) {
clear();
append(text);
}
function sendEnter() {
gSearchBox.focus();
EventUtils.sendKey("ENTER");
}
function sendEscape() {
gSearchBox.focus();
EventUtils.sendKey("ESCAPE");
}
function append(text) {
gSearchBox.focus();
for (let i = 0; i < text.length; i++) {
EventUtils.sendChar(text[i]);
}
info("Editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchView = null;
gSearchBox = null;
});

View File

@ -0,0 +1,62 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
var gScripts = null;
var gSearchBox = null;
var gSearchBoxPanel = null;
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
runTest();
});
gDebuggee.firstCall();
});
}
function runTest() {
gScripts = gDebugger.DebuggerView.Scripts;
gSearchBox = gScripts._searchbox;
gSearchBoxPanel = gScripts._searchboxPanel;
focusSearchbox();
}
function focusSearchbox() {
is(gSearchBoxPanel.state, "closed",
"The search box panel shouldn't be visible yet.");
gSearchBoxPanel.addEventListener("popupshown", function _onEvent(aEvent) {
gSearchBoxPanel.removeEventListener(aEvent.type, _onEvent);
is(gSearchBoxPanel.state, "open",
"The search box panel should be visible after searching started.");
closeDebuggerAndFinish();
});
EventUtils.sendMouseEvent({ type: "click" }, gSearchBox);
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
gScripts = null;
gSearchBox = null;
gSearchBoxPanel = null;
});

View File

@ -2,6 +2,6 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
function secondCall() {
// This comment is useful for browser_dbg_select-line.js.
// This comment is useful for browser_dbg_select-line.js.
eval("debugger;");
}

View File

@ -220,10 +220,6 @@ Highlighter.prototype = {
this.invalidateSize(!!aScroll);
if (this._highlighting) {
this.showOutline();
}
if (oldNode !== this.node) {
this.emitEvent("nodeselected");
}
@ -263,6 +259,7 @@ Highlighter.prototype = {
this.moveInfobar();
if (this._highlighting) {
this.showOutline();
this.emitEvent("highlighting");
}
},

View File

@ -18,6 +18,7 @@ Cu.import("resource:///modules/devtools/MarkupView.jsm");
Cu.import("resource:///modules/highlighter.jsm");
Cu.import("resource:///modules/devtools/LayoutView.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/EventEmitter.jsm");
// Inspector notifications dispatched through the nsIObserverService.
const INSPECTOR_NOTIFICATIONS = {
@ -67,7 +68,7 @@ function Inspector(aIUI)
this._IUI = aIUI;
this._winID = aIUI.winID;
this._browser = aIUI.browser;
this._listeners = {};
this._eventEmitter = new EventEmitter();
this._browser.addEventListener("resize", this, true);
@ -147,7 +148,7 @@ Inspector.prototype = {
this._destroyMarkup();
this._browser.removeEventListener("resize", this, true);
delete this._IUI;
delete this._listeners;
delete this._eventEmitter;
},
/**
@ -284,7 +285,7 @@ Inspector.prototype = {
this._markupBox.removeAttribute("hidden");
this.markup = new MarkupView(this, this._markupFrame);
this._emit("markuploaded");
this.emit("markuploaded");
},
_destroyMarkup: function Inspector__destroyMarkup()
@ -348,8 +349,7 @@ Inspector.prototype = {
delete this._frozen;
},
/// Event stuff. Would like to refactor this eventually.
/// Emulates the jetpack event source, which has a nice API.
/// Forward the events related calls to the event emitter.
/**
* Connect a listener to this object.
@ -361,10 +361,7 @@ Inspector.prototype = {
*/
on: function Inspector_on(aEvent, aListener)
{
if (!(aEvent in this._listeners)) {
this._listeners[aEvent] = [];
}
this._listeners[aEvent].push(aListener);
this._eventEmitter.on(aEvent, aListener);
},
/**
@ -377,11 +374,7 @@ Inspector.prototype = {
*/
once: function Inspector_once(aEvent, aListener)
{
let handler = function() {
this.removeListener(aEvent, handler);
aListener();
}.bind(this);
this.on(aEvent, handler);
this._eventEmitter.once(aEvent, aListener);
},
/**
@ -393,35 +386,18 @@ Inspector.prototype = {
* @param function aListener
* The listener to remove.
*/
removeListener: function Inspector_removeListener(aEvent, aListener)
off: function Inspector_removeListener(aEvent, aListener)
{
this._listeners[aEvent] = this._listeners[aEvent].filter(function(l) aListener != l);
this._eventEmitter.off(aEvent, aListener);
},
/**
* Emit an event on the inspector. All arguments to this method will
* be sent to listner functions.
*/
_emit: function Inspector__emit(aEvent)
emit: function Inspector_emit()
{
if (!(aEvent in this._listeners))
return;
let originalListeners = this._listeners[aEvent];
for (let listener of this._listeners[aEvent]) {
// If the inspector was destroyed during event emission, stop
// emitting.
if (!this._listeners) {
break;
}
// If listeners were removed during emission, make sure the
// event handler we're going to fire wasn't removed.
if (originalListeners === this._listeners[aEvent] ||
this._listeners[aEvent].some(function(l) l === listener)) {
listener.apply(null, arguments);
}
}
this._eventEmitter.emit.apply(this._eventEmitter, arguments);
}
}
@ -872,13 +848,13 @@ InspectorUI.prototype = {
this.inspecting = true;
this.highlighter.unlock();
this._notifySelected();
this._currentInspector._emit("unlocked");
this._currentInspector.emit("unlocked");
},
_notifySelected: function IUI__notifySelected(aFrom)
{
this._currentInspector._cancelLayoutChange();
this._currentInspector._emit("select", aFrom);
this._currentInspector.emit("select", aFrom);
},
/**
@ -908,7 +884,7 @@ InspectorUI.prototype = {
this.highlighter.lock();
this._notifySelected();
this._currentInspector._emit("locked");
this._currentInspector.emit("locked");
},
/**
@ -993,7 +969,7 @@ InspectorUI.prototype = {
this.highlighter.updateInfobar();
this.highlighter.invalidateSize();
this.breadcrumbs.updateSelectors();
this._currentInspector._emit("change", aUpdater);
this._currentInspector.emit("change", aUpdater);
},
/////////////////////////////////////////////////////////////////////////
@ -1821,8 +1797,8 @@ InspectorStyleSidebar.prototype = {
// If the current tool is already loaded, notify that we're
// showing this sidebar.
if (aTool.loaded) {
this._inspector._emit("sidebaractivated", aTool.id);
this._inspector._emit("sidebaractivated-" + aTool.id);
this._inspector.emit("sidebaractivated", aTool.id);
this._inspector.emit("sidebaractivated-" + aTool.id);
return;
}
@ -1841,14 +1817,14 @@ InspectorStyleSidebar.prototype = {
aTool.loaded = true;
aTool.context = aTool.registration.load(this._inspector, aTool.frame);
this._inspector._emit("sidebaractivated", aTool.id);
this._inspector.emit("sidebaractivated", aTool.id);
// Send an event specific to the activation of this panel. For
// this initial event, include a "createpanel" argument
// to let panels watch sidebaractivated to refresh themselves
// but ignore the one immediately after their load.
// I don't really like this, we should find a better solution.
this._inspector._emit("sidebaractivated-" + aTool.id, "createpanel");
this._inspector.emit("sidebaractivated-" + aTool.id, "createpanel");
}.bind(this);
aTool.frame.addEventListener("load", aTool.onLoad, true);
aTool.frame.setAttribute("src", aTool.registration.contentURL);

View File

@ -123,8 +123,8 @@ LayoutView.prototype = {
* Destroy the nodes. Remove listeners.
*/
destroy: function LV_destroy() {
this.inspector.removeListener("select", this.onSelect);
this.inspector.removeListener("unlocked", this.onUnlock);
this.inspector.off("select", this.onSelect);
this.inspector.off("unlocked", this.onUnlock);
this.browser.removeEventListener("MozAfterPaint", this.update, true);
this.iframe.removeEventListener("keypress", this.bound_handleKeypress, true);
this.inspector.chromeWindow.removeEventListener("message", this.onMessage, true);

View File

@ -331,7 +331,7 @@ MarkupView.prototype = {
this._updateChildren(container);
}
}
this._inspector._emit("markupmutation");
this._inspector.emit("markupmutation");
},
/**
@ -479,7 +479,7 @@ MarkupView.prototype = {
this._frame.removeEventListener("keydown", this._boundKeyDown, true);
delete this._boundKeyDown;
this._inspector.removeListener("select", this._boundSelect);
this._inspector.off("select", this._boundSelect);
delete this._boundSelect;
delete this._elt;

View File

@ -23,10 +23,10 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource:///modules/PropertyPanel.jsm");
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
Cu.import("resource://gre/modules/jsdebugger.jsm");
const SCRATCHPAD_CONTEXT_CONTENT = 1;
const SCRATCHPAD_CONTEXT_BROWSER = 2;
const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
@ -37,47 +37,6 @@ const BUTTON_POSITION_CANCEL = 1;
const BUTTON_POSITION_DONT_SAVE = 2;
const BUTTON_POSITION_REVERT=0;
let keysbundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
function SP_Pretty_Key(aElemKey) {
let elemString = "";
let elemMod = aElemKey.getAttribute("modifiers");
if (elemMod.match("accel")) {
if (navigator.platform.indexOf("Mac") !== -1) {
// XXX bug 779642 Use "Cmd-" literal vs cloverleaf meta-key until
// Orion adds variable height lines
// elemString += keysbundle.GetStringFromName("VK_META_CMD") +
// keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
elemString += "Cmd-";
} else {
elemString += keysbundle.GetStringFromName("VK_CONTROL") +
keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
}
}
if (elemMod.match("shift")) {
elemString += keysbundle.GetStringFromName("VK_SHIFT") +
keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("alt")) {
elemString += keysbundle.GetStringFromName("VK_ALT") +
keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("ctrl")) {
elemString += keysbundle.GetStringFromName("VK_CONTROL") +
keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("meta")) {
elemString += keysbundle.GetStringFromName("VK_META") +
keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
}
return elemString + aElemKey.getAttribute("key").toUpperCase();
}
/**
* The scratchpad object handles the Scratchpad window functionality.
*/
@ -268,6 +227,7 @@ var Scratchpad = {
this._contentSandbox = new Cu.Sandbox(contentWindow,
{ sandboxPrototype: contentWindow, wantXrays: false,
sandboxName: 'scratchpad-content'});
this._contentSandbox.__SCRATCHPAD__ = this;
this._previousBrowserWindow = this.browserWindow;
this._previousBrowser = this.gBrowser.selectedBrowser;
@ -302,6 +262,7 @@ var Scratchpad = {
this._chromeSandbox = new Cu.Sandbox(this.browserWindow,
{ sandboxPrototype: this.browserWindow, wantXrays: false,
sandboxName: 'scratchpad-chrome'});
this._chromeSandbox.__SCRATCHPAD__ = this;
addDebuggerToGlobal(this._chromeSandbox);
this._previousBrowserWindow = this.browserWindow;
@ -450,6 +411,34 @@ var Scratchpad = {
}
},
/**
* Reload the current page and execute the entire editor content when
* the page finishes loading. Note that this operation should be available
* only in the content context.
*/
reloadAndRun: function SP_reloadAndRun()
{
if (this.executionContext !== SCRATCHPAD_CONTEXT_CONTENT) {
Cu.reportError(this.strings.
GetStringFromName("scratchpadContext.invalid"));
return;
}
let browser = this.gBrowser.selectedBrowser;
this._reloadAndRunEvent = function onLoad(evt) {
if (evt.target !== browser.contentDocument) {
return;
}
browser.removeEventListener("load", this._reloadAndRunEvent, true);
this.run();
}.bind(this);
browser.addEventListener("load", this._reloadAndRunEvent, true);
browser.contentWindow.location.reload();
},
/**
* Execute the selected text (if any) or the entire editor content in the
* current context. The evaluation result is inserted into the editor after
@ -479,9 +468,9 @@ var Scratchpad = {
let insertionPoint = selection.start != selection.end ?
selection.end : // after selected text
this.editor.getCharCount(); // after text end
let newComment = "\n/*\n" + aValue + "\n*/";
this.setText(newComment, insertionPoint, insertionPoint);
// Select the new comment.
@ -510,7 +499,7 @@ var Scratchpad = {
else if (aError.lineNumber) {
stack = "@" + aError.lineNumber;
}
let newComment = "Exception: " + ( aError.message || aError) + ( stack == "" ? stack : "\n" + stack.replace(/\n$/, "") );
this.writeAsComment(newComment);
@ -931,7 +920,6 @@ var Scratchpad = {
*/
revertFile: function SP_revertFile(aCallback)
{
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(this.filename);
@ -1019,6 +1007,7 @@ var Scratchpad = {
let content = document.getElementById("sp-menu-content");
document.getElementById("sp-menu-browser").removeAttribute("checked");
document.getElementById("sp-cmd-reloadAndRun").removeAttribute("disabled");
content.setAttribute("checked", true);
this.executionContext = SCRATCHPAD_CONTEXT_CONTENT;
this.notificationBox.removeAllNotifications(false);
@ -1035,8 +1024,12 @@ var Scratchpad = {
}
let browser = document.getElementById("sp-menu-browser");
let reloadAndRun = document.getElementById("sp-cmd-reloadAndRun");
document.getElementById("sp-menu-content").removeAttribute("checked");
reloadAndRun.setAttribute("disabled", true);
browser.setAttribute("checked", true);
this.executionContext = SCRATCHPAD_CONTEXT_BROWSER;
this.notificationBox.appendNotification(
this.strings.GetStringFromName("browserContext.notification"),
@ -1097,9 +1090,9 @@ var Scratchpad = {
let initialText = this.strings.formatStringFromName(
"scratchpadIntro1",
[SP_Pretty_Key(document.getElementById("sp-key-run")),
SP_Pretty_Key(document.getElementById("sp-key-inspect")),
SP_Pretty_Key(document.getElementById("sp-key-display"))],
[LayoutHelpers.prettyKey(document.getElementById("sp-key-run")),
LayoutHelpers.prettyKey(document.getElementById("sp-key-inspect")),
LayoutHelpers.prettyKey(document.getElementById("sp-key-display"))],
3);
if ("arguments" in window &&
@ -1213,6 +1206,8 @@ var Scratchpad = {
}
this.resetContext();
this.gBrowser.selectedBrowser.removeEventListener("load",
this._reloadAndRunEvent, true);
this.editor.removeEventListener(SourceEditor.EVENTS.DIRTY_CHANGED,
this._onDirtyChanged);
PreferenceObserver.uninit();

View File

@ -45,6 +45,7 @@
<command id="sp-cmd-display" oncommand="Scratchpad.display();"/>
<command id="sp-cmd-contentContext" oncommand="Scratchpad.setContentContext();"/>
<command id="sp-cmd-browserContext" oncommand="Scratchpad.setBrowserContext();" disabled="true"/>
<command id="sp-cmd-reloadAndRun" oncommand="Scratchpad.reloadAndRun();"/>
<command id="sp-cmd-resetContext" oncommand="Scratchpad.resetContext();"/>
<command id="sp-cmd-errorConsole" oncommand="Scratchpad.openErrorConsole();" disabled="true"/>
<command id="sp-cmd-webConsole" oncommand="Scratchpad.openWebConsole();"/>
@ -90,6 +91,10 @@
key="&display.key;"
command="sp-cmd-display"
modifiers="accel"/>
<key id="sp-key-reloadAndRun"
key="&reloadAndRun.key;"
command="sp-cmd-reloadAndRun"
modifiers="accel,shift"/>
<key id="sp-key-errorConsole"
key="&errorConsoleCmd.commandkey;"
command="sp-cmd-errorConsole"
@ -194,6 +199,11 @@
key="sp-key-display"
command="sp-cmd-display"/>
<menuseparator/>
<menuitem id="sp-text-reloadAndRun"
label="&reloadAndRun.label;"
key="sp-key-reloadAndRun"
accesskey="&reloadAndRun.accesskey;"
command="sp-cmd-reloadAndRun"/>
<menuitem id="sp-text-resetContext"
label="&resetContext2.label;"
accesskey="&resetContext2.accesskey;"

View File

@ -34,6 +34,7 @@ MOCHITEST_BROWSER_FILES = \
browser_scratchpad_bug_651942_recent_files.js \
browser_scratchpad_bug756681_display_non_error_exceptions.js \
browser_scratchpad_bug_751744_revert_to_saved.js \
browser_scratchpad_bug740948_reload_and_run.js \
head.js \
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,73 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
let EDITOR_TEXT = [
"var evt = new CustomEvent('foo', { bubbles: true });",
"document.body.innerHTML = 'Modified text';",
"window.dispatchEvent(evt);"
].join("\n");
function test()
{
waitForExplicitFinish();
Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, true);
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,Scratchpad test for bug 740948";
}
function runTests()
{
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
// Test that Reload And Run command is enabled in the content
// context and disabled in the browser context.
let reloadAndRun = gScratchpadWindow.document.
getElementById("sp-cmd-reloadAndRun");
ok(reloadAndRun, "Reload And Run command exists");
ok(!reloadAndRun.hasAttribute("disabled"),
"Reload And Run command is enabled");
sp.setBrowserContext();
ok(reloadAndRun.hasAttribute("disabled"),
"Reload And Run command is disabled in the browser context.");
// Switch back to the content context and run our predefined
// code. This code modifies the body of our document and dispatches
// a custom event 'foo'. We listen to that event and check the
// body to make sure that the page has been reloaded and Scratchpad
// code has been executed.
sp.setContentContext();
sp.setText(EDITOR_TEXT);
let browser = gBrowser.selectedBrowser;
browser.addEventListener("DOMWindowCreated", function onWindowCreated() {
browser.removeEventListener("DOMWindowCreated", onWindowCreated, true);
browser.contentWindow.addEventListener("foo", function onFoo() {
browser.contentWindow.removeEventListener("foo", onFoo, true);
is(browser.contentWindow.document.body.innerHTML, "Modified text",
"After reloading, HTML is different.");
Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
finish();
}, true);
}, true);
ok(browser.contentWindow.document.body.innerHTML !== "Modified text",
"Before reloading, HTML is intact.");
sp.reloadAndRun();
}

View File

@ -41,6 +41,14 @@ function runTests()
ok(!notificationBox.currentNotification,
"there is no notification in content context");
let dsp = sp.contentSandbox.__SCRATCHPAD__;
ok(sp.contentSandbox.__SCRATCHPAD__,
"there is a variable named __SCRATCHPAD__");
ok(sp.contentSandbox.__SCRATCHPAD__.editor,
"scratchpad is actually an instance of Scratchpad");
sp.setText("window.foobarBug636725 = 'aloha';");
ok(!content.wrappedJSObject.foobarBug636725,
@ -62,6 +70,12 @@ function runTests()
isnot(contentMenu.getAttribute("checked"), "true",
"content menuitem is not checked");
ok(sp.chromeSandbox.__SCRATCHPAD__,
"there is a variable named __SCRATCHPAD__");
ok(sp.chromeSandbox.__SCRATCHPAD__.editor,
"scratchpad is actually an instance of Scratchpad");
ok(notificationBox.currentNotification,
"there is a notification in browser context");

View File

@ -0,0 +1,80 @@
var EXPORTED_SYMBOLS = ["EventEmitter"];
function EventEmitter() {
}
EventEmitter.prototype = {
/**
* Connect a listener.
*
* @param string aEvent
* The event name to which we're connecting.
* @param function aListener
* Called when the event is fired.
*/
on: function EventEmitter_on(aEvent, aListener) {
if (!this._eventEmitterListeners)
this._eventEmitterListeners = new Map();
if (!this._eventEmitterListeners.has(aEvent)) {
this._eventEmitterListeners.set(aEvent, []);
}
this._eventEmitterListeners.get(aEvent).push(aListener);
},
/**
* Listen for the next time an event is fired.
*
* @param string aEvent
* The event name to which we're connecting.
* @param function aListener
* Called when the event is fired. Will be called at most one time.
*/
once: function EventEmitter_once(aEvent, aListener) {
let handler = function() {
this.off(aEvent, handler);
aListener();
}.bind(this);
this.on(aEvent, handler);
},
/**
* Remove a previously-registered event listener. Works for events
* registered with either on or once.
*
* @param string aEvent
* The event name whose listener we're disconnecting.
* @param function aListener
* The listener to remove.
*/
off: function EventEmitter_off(aEvent, aListener) {
if (!this._eventEmitterListeners)
return;
let listeners = this._eventEmitterListeners.get(aEvent);
this._eventEmitterListeners.set(aEvent, listeners.filter(function(l) aListener != l));
},
/**
* Emit an event. All arguments to this method will
* be sent to listner functions.
*/
emit: function EventEmitter_emit(aEvent) {
if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(aEvent))
return;
let originalListeners = this._eventEmitterListeners.get(aEvent);
for (let listener of this._eventEmitterListeners.get(aEvent)) {
// If the object was destroyed during event emission, stop
// emitting.
if (!this._eventEmitterListeners) {
break;
}
// If listeners were removed during emission, make sure the
// event handler we're going to fire wasn't removed.
if (originalListeners === this._eventEmitterListeners.get(aEvent) ||
this._eventEmitterListeners.get(aEvent).some(function(l) l === listener)) {
listener.apply(null, arguments);
}
}
},
}

View File

@ -8,6 +8,16 @@ const Cu = Components.utils;
const Ci = Components.interfaces;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "PlatformKeys", function() {
return Services.strings.createBundle(
"chrome://global-platform/locale/platformKeys.properties");
});
var EXPORTED_SYMBOLS = ["LayoutHelpers"];
LayoutHelpers = {
@ -310,4 +320,60 @@ LayoutHelpers = {
return false;
}
},
/**
* Prettifies the modifier keys for an element.
*
* @param Node aElemKey
* The key element to get the modifiers from.
* @return string
* A prettified and properly separated modifier keys string.
*/
prettyKey: function LH_prettyKey(aElemKey)
{
let elemString = "";
let elemMod = aElemKey.getAttribute("modifiers");
if (elemMod.match("accel")) {
if (Services.appinfo.OS == "Darwin") {
// XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
// Orion adds variable height lines.
// elemString += PlatformKeys.GetStringFromName("VK_META") +
// PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
elemString += "Cmd-";
} else {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
}
if (elemMod.match("access")) {
if (Services.appinfo.OS == "Darwin") {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
} else {
elemString += PlatformKeys.GetStringFromName("VK_ALT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
}
if (elemMod.match("shift")) {
elemString += PlatformKeys.GetStringFromName("VK_SHIFT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("alt")) {
elemString += PlatformKeys.GetStringFromName("VK_ALT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("ctrl")) {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("meta")) {
elemString += PlatformKeys.GetStringFromName("VK_META") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
return elemString +
(aElemKey.getAttribute("keycode").replace(/^.*VK_/, "") ||
aElemKey.getAttribute("key")).toUpperCase();
}
};

View File

@ -22,6 +22,7 @@ MOCHITEST_BROWSER_FILES = \
browser_toolbar_tooltip.js \
browser_toolbar_webconsole_errors_count.js \
browser_layoutHelpers.js \
browser_eventemitter_basic.js \
head.js \
helpers.js \
leakhunt.js \

View File

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
Cu.import("resource:///modules/devtools/EventEmitter.jsm", this);
let emitter = new EventEmitter();
ok(emitter, "We have an event emitter");
emitter.on("next", next);
emitter.emit("next", "abc", "def");
let beenHere1 = false;
function next(eventName, str1, str2) {
is(eventName, "next", "Got event");
is(str1, "abc", "Argument 1 is correct");
is(str2, "def", "Argument 2 is correct");
ok(!beenHere1, "first time in next callback");
beenHere1 = true;
emitter.off("next", next);
emitter.emit("next");
emitter.once("onlyonce", onlyOnce);
emitter.emit("onlyonce");
emitter.emit("onlyonce");
}
let beenHere2 = false;
function onlyOnce() {
ok(!beenHere2, "\"once\" listner has been called once");
beenHere2 = true;
emitter.emit("onlyonce");
killItWhileEmitting();
}
function killItWhileEmitting() {
function c1() {
ok(true, "c1 called");
}
function c2() {
ok(true, "c2 called");
emitter.off("tick", c3);
}
function c3() {
ok(false, "c3 should not be called");
}
function c4() {
ok(true, "c4 called");
}
emitter.on("tick", c1);
emitter.on("tick", c2);
emitter.on("tick", c3);
emitter.on("tick", c4);
emitter.emit("tick");
delete emitter;
finish();
}
}

View File

@ -146,9 +146,9 @@ RuleViewTool.prototype = {
},
destroy: function RVT_destroy() {
this.inspector.removeListener("select", this._onSelect);
this.inspector.removeListener("change", this._onChange);
this.inspector.removeListener("sidebaractivated-ruleview", this._onChange);
this.inspector.off("select", this._onSelect);
this.inspector.off("change", this._onChange);
this.inspector.off("sidebaractivated-ruleview", this._onChange);
this.view.element.removeEventListener("CssRuleViewChanged",
this._changeHandler);
this.view.element.removeEventListener("CssRuleViewCSSLinkClicked",
@ -214,9 +214,9 @@ ComputedViewTool.prototype = {
destroy: function CVT_destroy(aContext)
{
this.inspector.removeListener("select", this._onSelect);
this.inspector.removeListener("change", this._onChange);
this.inspector.removeListener("sidebaractivated-computedview", this._onChange);
this.inspector.off("select", this._onSelect);
this.inspector.off("change", this._onChange);
this.inspector.off("sidebaractivated-computedview", this._onChange);
this.view.destroy();
delete this.view;

View File

@ -647,8 +647,8 @@ function JSTermHelper(aJSTerm)
{
aJSTerm.helperEvaluated = true;
aJSTerm.window.open(
"https://developer.mozilla.org/AppLinks/WebConsoleHelp?locale=" +
aJSTerm.window.navigator.language, "help", "");
"https://developer.mozilla.org/docs/Tools/Web_Console/Helpers",
"help", "");
};
/**

View File

@ -1,34 +1,178 @@
Copyright (c) 2011 Mozilla Foundation
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Contributors: Andreas Gal <gal@mozilla.com>
Chris G Jones <cjones@mozilla.com>
Shaon Barman <shaon.barman@gmail.com>
Vivien Nicolas <21@vingtetun.org>
Justin D'Arcangelo <justindarc@gmail.com>
Yury Delendik
Kalervo Kujala
Adil Allawi <@ironymark>
Jakob Miland <saebekassebil@gmail.com>
Artur Adib <aadib@mozilla.com>
Brendan Dahl <bdahl@mozilla.com>
David Quintana <gigaherz@gmail.com>
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -1,4 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 0.4.11
Current extension version is: 0.5.22

View File

@ -1,5 +1,19 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
@ -18,6 +32,7 @@ const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
const MAX_DATABASE_LENGTH = 4096;
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
const METRO_ID = '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}';
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
@ -36,7 +51,8 @@ if (appInfo.ID === FIREFOX_ID) {
privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
.getService(Ci.nsIPrivateBrowsingService);
inPrivateBrowsing = privateBrowsing.privateBrowsingEnabled;
} else if (appInfo.ID === SEAMONKEY_ID) {
} else if (appInfo.ID === SEAMONKEY_ID ||
appInfo.ID === METRO_ID) {
privateBrowsing = null;
inPrivateBrowsing = false;
}

View File

@ -1,3 +1,18 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var EXPORTED_SYMBOLS = ["PdfJs"];
const Cc = Components.classes;

View File

@ -1,5 +1,19 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';

View File

@ -1,3 +1,18 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* {
padding: 0;
margin: 0;
@ -29,29 +44,17 @@ select {
#viewerContainer:-webkit-full-screen {
top: 0px;
padding-top: 6px;
padding-bottom: 24px;
border-top: 5px solid transparent;
background-color: #404040;
background-image: url(images/texture.png);
width: 100%;
height: 100%;
overflow: auto;
}
:-webkit-full-screen #viewer {
margin: 0pt;
padding: 0pt;
height: 100%;
width: 100%;
overflow: hidden;
}
:-webkit-full-screen .page {
margin: 0px auto;
margin-bottom: 10px;
}
#viewerContainer:-moz-full-screen {
top: 0px;
border-top: 5px solid transparent;
background-color: #404040;
background-image: url(images/texture.png);
width: 100%;
@ -59,6 +62,10 @@ select {
overflow: hidden;
}
:-webkit-full-screen .page:last-child {
margin-bottom: 40px;
}
:-moz-full-screen .page:last-child {
margin-bottom: 40px;
}
@ -760,9 +767,9 @@ html[dir='rtl'] .toolbarButton.pageDown::before {
}
#thumbnailView {
position: fixed;
position: absolute;
width: 120px;
top: 33px;
top: 0;
bottom: 0;
padding: 10px 40px 0;
overflow: auto;
@ -771,8 +778,6 @@ html[dir='rtl'] .toolbarButton.pageDown::before {
.thumbnail {
margin-bottom: 15px;
float: left;
width: 114px;
height: 142px;
}
.thumbnail:not([data-loaded]) {
@ -825,9 +830,9 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
}
#outlineView {
position: fixed;
position: absolute;
width: 192px;
top: 33px;
top: 0;
bottom: 0;
padding: 4px 4px 0;
overflow: auto;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,19 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
@ -21,6 +35,7 @@ var RenderingStates = {
FINISHED: 3
};
PDFJS.workerSrc = '../build/pdf.js';
var mozL10n = document.mozL10n || document.webL10n;
@ -33,6 +48,19 @@ function getFileName(url) {
return url.substring(url.lastIndexOf('/', end) + 1, end);
}
function scrollIntoView(element, spot) {
var parent = element.offsetParent, offsetY = element.offsetTop;
while (parent.clientHeight == parent.scrollHeight) {
offsetY += parent.offsetTop;
parent = parent.offsetParent;
if (!parent)
return; // no need to scroll
}
if (spot)
offsetY += spot.top;
parent.scrollTop = offsetY;
}
var Cache = function cacheCache(size) {
var data = [];
this.push = function cachePush(view) {
@ -98,6 +126,21 @@ var ProgressBar = (function ProgressBarClosure() {
return ProgressBar;
})();
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var FirefoxCom = (function FirefoxComClosure() {
return {
/**
@ -242,6 +285,7 @@ var PDFView = {
thumbnailViewScroll: null,
isFullscreen: false,
previousScale: null,
pageRotation: 0,
// called once when the document is loaded
initialize: function pdfViewInitialize() {
@ -431,7 +475,13 @@ var PDFView = {
setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
this.url = url;
document.title = decodeURIComponent(getFileName(url)) || url;
try {
document.title = decodeURIComponent(getFileName(url)) || url;
} catch (e) {
// decodeURIComponent may throw URIError,
// fall back to using the unprocessed url in that case
document.title = url;
}
},
open: function pdfViewOpen(url, scale, password) {
@ -674,6 +724,8 @@ var PDFView = {
storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
}
this.pageRotation = 0;
var pages = this.pages = [];
this.pageText = [];
this.startedTextExtraction = false;
@ -1147,6 +1199,34 @@ var PDFView = {
this.isFullscreen = false;
this.parseScale(this.previousScale);
this.page = this.page;
},
rotatePages: function pdfViewPageRotation(delta) {
this.pageRotation = (this.pageRotation + 360 + delta) % 360;
for (var i = 0, l = this.pages.length; i < l; i++) {
var page = this.pages[i];
page.update(page.scale, this.pageRotation);
}
for (var i = 0, l = this.thumbnails.length; i < l; i++) {
var thumb = this.thumbnails[i];
thumb.updateRotation(this.pageRotation);
}
var currentPage = this.pages[this.page - 1];
if (this.isFullscreen) {
this.parseScale('page-fit', true);
}
this.renderHighestPriority();
// Wait for fullscreen to take effect
setTimeout(function() {
currentPage.scrollIntoView();
}, 0);
}
};
@ -1155,8 +1235,9 @@ var PageView = function pageView(container, pdfPage, id, scale,
this.id = id;
this.pdfPage = pdfPage;
this.rotation = 0;
this.scale = scale || 1.0;
this.viewport = this.pdfPage.getViewport(this.scale);
this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
@ -1167,6 +1248,8 @@ var PageView = function pageView(container, pdfPage, id, scale,
var div = this.el = document.createElement('div');
div.id = 'pageContainer' + this.id;
div.className = 'page';
div.style.width = this.viewport.width + 'px';
div.style.height = this.viewport.height + 'px';
container.appendChild(anchor);
container.appendChild(div);
@ -1176,12 +1259,18 @@ var PageView = function pageView(container, pdfPage, id, scale,
this.pdfPage.destroy();
};
this.update = function pageViewUpdate(scale) {
this.update = function pageViewUpdate(scale, rotation) {
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
if (typeof rotation !== 'undefined') {
this.rotation = rotation;
}
this.scale = scale || this.scale;
var viewport = this.pdfPage.getViewport(this.scale);
var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
var viewport = this.pdfPage.getViewport(this.scale, totalRotation);
this.viewport = viewport;
div.style.width = viewport.width + 'px';
@ -1310,7 +1399,7 @@ var PageView = function pageView(container, pdfPage, id, scale,
this.scrollIntoView = function pageViewScrollIntoView(dest) {
if (!dest) {
div.scrollIntoView(true);
scrollIntoView(div);
return;
}
@ -1369,16 +1458,7 @@ var PageView = function pageView(container, pdfPage, id, scale,
var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]);
var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]);
// using temporary div to scroll it into view
var tempDiv = document.createElement('div');
tempDiv.style.position = 'absolute';
tempDiv.style.left = Math.floor(x) + 'px';
tempDiv.style.top = Math.floor(y) + 'px';
tempDiv.style.width = Math.ceil(width) + 'px';
tempDiv.style.height = Math.ceil(height) + 'px';
div.appendChild(tempDiv);
tempDiv.scrollIntoView(true);
div.removeChild(tempDiv);
scrollIntoView(div, {left: x, top: y, width: width, height: height});
}, 0);
};
@ -1521,7 +1601,9 @@ var ThumbnailView = function thumbnailView(container, pdfPage, id) {
return false;
};
var viewport = pdfPage.getViewport(1);
var rotation = 0;
var totalRotation = (rotation + pdfPage.rotate) % 360;
var viewport = pdfPage.getViewport(1, totalRotation);
var pageWidth = this.width = viewport.width;
var pageHeight = this.height = viewport.height;
var pageRatio = pageWidth / pageHeight;
@ -1536,12 +1618,41 @@ var ThumbnailView = function thumbnailView(container, pdfPage, id) {
div.id = 'thumbnailContainer' + id;
div.className = 'thumbnail';
var ring = document.createElement('div');
ring.className = 'thumbnailSelectionRing';
ring.style.width = canvasWidth + 'px';
ring.style.height = canvasHeight + 'px';
div.appendChild(ring);
anchor.appendChild(div);
container.appendChild(anchor);
this.hasImage = false;
this.renderingState = RenderingStates.INITIAL;
this.updateRotation = function(rot) {
rotation = rot;
totalRotation = (rotation + pdfPage.rotate) % 360;
viewport = pdfPage.getViewport(1, totalRotation);
pageWidth = this.width = viewport.width;
pageHeight = this.height = viewport.height;
pageRatio = pageWidth / pageHeight;
canvasHeight = canvasWidth / this.width * this.height;
scaleX = this.scaleX = (canvasWidth / pageWidth);
scaleY = this.scaleY = (canvasHeight / pageHeight);
div.removeAttribute('data-loaded');
ring.textContent = '';
ring.style.width = canvasWidth + 'px';
ring.style.height = canvasHeight + 'px';
this.hasImage = false;
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
}
function getPageDrawContext() {
var canvas = document.createElement('canvas');
canvas.id = 'thumbnail' + id;
@ -1555,10 +1666,7 @@ var ThumbnailView = function thumbnailView(container, pdfPage, id) {
div.setAttribute('data-loaded', true);
var ring = document.createElement('div');
ring.className = 'thumbnailSelectionRing';
ring.appendChild(canvas);
div.appendChild(ring);
var ctx = canvas.getContext('2d');
ctx.save();
@ -1584,7 +1692,7 @@ var ThumbnailView = function thumbnailView(container, pdfPage, id) {
var self = this;
var ctx = getPageDrawContext();
var drawViewport = pdfPage.getViewport(scaleX);
var drawViewport = pdfPage.getViewport(scaleX, totalRotation);
var renderContext = {
canvasContext: ctx,
viewport: drawViewport,
@ -1890,6 +1998,93 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
PDFView.renderHighestPriority();
});
document.getElementById('viewThumbnail').addEventListener('click',
function() {
PDFView.switchSidebarView('thumbs');
});
document.getElementById('viewOutline').addEventListener('click',
function() {
PDFView.switchSidebarView('outline');
});
document.getElementById('viewSearch').addEventListener('click',
function() {
PDFView.switchSidebarView('search');
});
document.getElementById('searchButton').addEventListener('click',
function() {
PDFView.search();
});
document.getElementById('previous').addEventListener('click',
function() {
PDFView.page--;
});
document.getElementById('next').addEventListener('click',
function() {
PDFView.page++;
});
document.querySelector('.zoomIn').addEventListener('click',
function() {
PDFView.zoomIn();
});
document.querySelector('.zoomOut').addEventListener('click',
function() {
PDFView.zoomOut();
});
document.getElementById('fullscreen').addEventListener('click',
function() {
PDFView.fullscreen();
});
document.getElementById('openFile').addEventListener('click',
function() {
document.getElementById('fileInput').click();
});
document.getElementById('print').addEventListener('click',
function() {
window.print();
});
document.getElementById('download').addEventListener('click',
function() {
PDFView.download();
});
document.getElementById('searchTermsInput').addEventListener('keydown',
function() {
if (event.keyCode == 13) {
PDFView.search();
}
});
document.getElementById('pageNumber').addEventListener('change',
function() {
PDFView.page = this.value;
});
document.getElementById('scaleSelect').addEventListener('change',
function() {
PDFView.parseScale(this.value);
});
document.getElementById('page_rotate_ccw').addEventListener('click',
function() {
PDFView.rotatePages(-90);
});
document.getElementById('page_rotate_cw').addEventListener('click',
function() {
PDFView.rotatePages(90);
});
if (FirefoxCom.requestSync('getLoadingType') == 'passive') {
PDFView.setTitleUsingUrl(file);
PDFView.initPassiveLoading();
@ -2050,7 +2245,7 @@ window.addEventListener('pagechange', function pagechange(evt) {
var last = numVisibleThumbs > 1 ?
visibleThumbs.last.id : first;
if (page <= first || page >= last)
thumbnail.scrollIntoView();
scrollIntoView(thumbnail);
}
}
@ -2137,6 +2332,18 @@ window.addEventListener('keydown', function keydown(evt) {
handled = true;
}
break;
case 82: // 'r'
PDFView.rotatePages(90);
break;
}
}
if (cmd == 4) { // shift-key
switch (evt.keyCode) {
case 82: // 'r'
PDFView.rotatePages(-90);
break;
}
}

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