author | David Anderson <danderson@mozilla.com> |
Wed, 25 Jan 2012 15:27:24 -0800 | |
changeset 105611 | 18f53c3f6fac1ab4e7aa1f9509dfd8521dc7981a |
parent 105610 | aec2f378af30cd816035b89478aa96d0ba730594 (current diff) |
parent 85343 | edf8075b0333a0ed7be2d77b1fd06bc87ca7bdec (diff) |
child 105612 | b2a4c499d052ad6d4da96dedca8667144bb6ab1a |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 12.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.hgtags +++ b/.hgtags @@ -67,8 +67,9 @@ 9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220 +462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
--- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -611,16 +611,19 @@ nsAccessible::VisibilityState() const nsIView* view = frame->GetView(); if (view && view->GetVisibility() == nsViewVisibility_kHide) return vstates; } while (accessible = accessible->Parent()); nsIFrame* frame = GetFrame(); + if (!frame) + return vstates; + const nsCOMPtr<nsIPresShell> shell(GetPresShell()); // We need to know if at least a kMinPixels around the object is visible, // otherwise it will be marked states::OFFSCREEN. const PRUint16 kMinPixels = 12; const nsSize frameSize = frame->GetSize(); const nsRectVisibility rectVisibility = shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
--- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -1340,17 +1340,17 @@ nsHTMLTableAccessible::HasDescendant(con return true; // Make sure that the item we found has contents and either has multiple // children or the found item is not a whitespace-only text node. nsCOMPtr<nsIContent> foundItemContent = do_QueryInterface(foundItem); if (foundItemContent->GetChildCount() > 1) return true; // Treat multiple child nodes as non-empty - nsIContent *innerItemContent = foundItemContent->GetChildAt(0); + nsIContent *innerItemContent = foundItemContent->GetFirstChild(); if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace()) return true; // If we found more than one node then return true not depending on // aAllowEmpty flag. // XXX it might be dummy but bug 501375 where we changed this addresses // performance problems only. Note, currently 'aAllowEmpty' flag is used for // caption element only. On another hand we create accessible object for
--- a/accessible/src/mac/Makefile.in +++ b/accessible/src/mac/Makefile.in @@ -51,16 +51,17 @@ LIBXUL_LIBRARY = 1 CMMSRCS = nsAccessNodeWrap.mm \ nsDocAccessibleWrap.mm \ nsRootAccessibleWrap.mm \ nsAccessibleWrap.mm \ mozAccessible.mm \ mozDocAccessible.mm \ mozActionElements.mm \ mozTextAccessible.mm \ + mozHTMLAccessible.mm \ $(NULL) EXPORTS = \ nsAccessNodeWrap.h \ nsTextAccessibleWrap.h \ nsAccessibleWrap.h \ nsARIAGridAccessibleWrap.h \
new file mode 100644 --- /dev/null +++ b/accessible/src/mac/mozHTMLAccessible.h @@ -0,0 +1,45 @@ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Original Author: Hub Figuière <hub@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#import "mozAccessible.h" + +@interface mozHeadingAccessible : mozAccessible + +@end
new file mode 100644 --- /dev/null +++ b/accessible/src/mac/mozHTMLAccessible.mm @@ -0,0 +1,56 @@ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Original Author: Hub Figuière <hub@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#import "mozHTMLAccessible.h" + +#import "nsHyperTextAccessible.h" + +@implementation mozHeadingAccessible + +- (id)value +{ + if (!mGeckoAccessible || !mGeckoAccessible->IsHyperText()) + return nil; + + PRUint32 level = mGeckoAccessible->AsHyperText()->GetLevelInternal(); + return [NSNumber numberWithInt:level]; +} + +@end
--- a/accessible/src/mac/mozTextAccessible.mm +++ b/accessible/src/mac/mozTextAccessible.mm @@ -1,22 +1,25 @@ #include "nsAccessibleWrap.h" + +#include "nsCocoaUtils.h" #include "nsObjCExceptions.h" #import "mozTextAccessible.h" using namespace mozilla::a11y; @interface mozTextAccessible (Private) - (NSString*)subrole; - (NSString*)selectedText; - (NSValue*)selectedTextRange; - (long)textLength; - (BOOL)isReadOnly; - (void)setText:(NSString*)newText; +- (NSString*)text; @end @implementation mozTextAccessible - (id)initWithAccessible:(nsAccessibleWrap*)accessible { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; @@ -74,18 +77,19 @@ using namespace mozilla::a11y; if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) return [NSNumber numberWithInt:[self textLength]]; if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) return [self selectedTextRange]; if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) return [self selectedText]; // Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText // object's AXSelectedText attribute. See bug 674612. + // Also if there is no selected text, we return the full text.See bug 369710 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) - return [self selectedText]; + return [self selectedText] ? : [self text]; // let mozAccessible handle all other attributes return [super accessibilityAttributeValue:attribute]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute @@ -153,16 +157,30 @@ using namespace mozilla::a11y; if (mGeckoEditableTextAccessible) { mGeckoEditableTextAccessible->SetTextContents(NS_ConvertUTF8toUTF16([newString UTF8String])); } NS_OBJC_END_TRY_ABORT_BLOCK; } +- (NSString*)text +{ + if (!mGeckoTextAccessible) + return nil; + + nsAutoString text; + nsresult rv = + mGeckoTextAccessible->GetText(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, + text); + NS_ENSURE_SUCCESS(rv, nil); + + return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text); +} + - (long)textLength { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; return mGeckoTextAccessible ? mGeckoTextAccessible->CharacterCount() : 0; NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); }
--- a/accessible/src/mac/nsAccessibleWrap.mm +++ b/accessible/src/mac/nsAccessibleWrap.mm @@ -40,16 +40,17 @@ #include "nsObjCExceptions.h" #import "nsRoleMap.h" #include "Role.h" #import "mozAccessible.h" #import "mozActionElements.h" +#import "mozHTMLAccessible.h" #import "mozTextAccessible.h" using namespace mozilla::a11y; nsAccessibleWrap:: nsAccessibleWrap(nsIContent *aContent, nsIWeakReference *aShell) : nsAccessible(aContent, aShell), mNativeObject(nil), mNativeInited(false) @@ -103,20 +104,22 @@ nsAccessibleWrap::GetNativeType () [mozButtonAccessible class]; } case roles::CHECKBUTTON: return [mozCheckboxAccessible class]; case roles::AUTOCOMPLETE: return [mozComboboxAccessible class]; - + + case roles::HEADING: + return [mozHeadingAccessible class]; + case roles::ENTRY: case roles::STATICTEXT: - case roles::HEADING: case roles::LABEL: case roles::CAPTION: case roles::ACCEL_LABEL: case roles::TEXT_LEAF: // normal textfield (static or editable) return [mozTextAccessible class]; case roles::COMBOBOX:
--- a/accessible/src/mac/nsRoleMap.h +++ b/accessible/src/mac/nsRoleMap.h @@ -142,17 +142,17 @@ static const NSString* AXRoles [] = { NSAccessibilityGroupRole, // ROLE_FOOTER NSAccessibilityGroupRole, // ROLE_PARAGRAPH @"AXRuler", // ROLE_RULER. 10.4+ only, so we re-define the constant. NSAccessibilityComboBoxRole, // ROLE_AUTOCOMPLETE NSAccessibilityTextFieldRole, // ROLE_EDITBAR NSAccessibilityTextFieldRole, // ROLE_ENTRY NSAccessibilityStaticTextRole, // ROLE_CAPTION @"AXWebArea", // ROLE_DOCUMENT_FRAME - NSAccessibilityStaticTextRole, // ROLE_HEADING + @"AXHeading", // ROLE_HEADING NSAccessibilityGroupRole, // ROLE_PAGE NSAccessibilityGroupRole, // ROLE_SECTION NSAccessibilityUnknownRole, // ROLE_REDUNDANT_OBJECT NSAccessibilityGroupRole, // ROLE_FORM NSAccessibilityUnknownRole, // ROLE_IME NSAccessibilityUnknownRole, // ROLE_APP_ROOT. unused on OS X NSAccessibilityMenuItemRole, // ROLE_PARENT_MENUITEM NSAccessibilityGroupRole, // ROLE_CALENDAR
--- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -390,20 +390,18 @@ @BINPATH@/components/contentSecurityPolicy.js @BINPATH@/components/contentAreaDropListener.manifest @BINPATH@/components/contentAreaDropListener.js @BINPATH@/components/messageWakeupService.js @BINPATH@/components/messageWakeupService.manifest @BINPATH@/components/nsFilePicker.js @BINPATH@/components/nsFilePicker.manifest #ifdef MOZ_B2G_RIL -@BINPATH@/components/nsTelephonyWorker.manifest -@BINPATH@/components/nsTelephonyWorker.js -@BINPATH@/components/Telephony.manifest -@BINPATH@/components/Telephony.js +@BINPATH@/components/RadioInterfaceLayer.manifest +@BINPATH@/components/RadioInterfaceLayer.js @BINPATH@/components/nsWifiWorker.js @BINPATH@/components/nsWifiWorker.manifest #endif #ifdef XP_MACOSX @BINPATH@/components/libalerts.dylib #endif #ifdef MOZ_ENABLE_DBUS @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1496,16 +1496,18 @@ function prepareForStartup() { gBrowser.addEventListener("MozApplicationManifest", OfflineApps, false); // setup simple gestures support gGestureSupport.init(true); } function delayedStartup(isLoadingBlank, mustLoadSidebar) { + Cu.import("resource:///modules/TelemetryTimestamps.jsm"); + TelemetryTimestamps.add("delayedStartupStarted"); gDelayedStartupTimeoutId = null; Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false); @@ -1760,16 +1762,17 @@ function delayedStartup(isLoadingBlank, Ci.nsIPrefLocalizedString).data) document.getElementById("appmenu_charsetMenu").hidden = true; #endif window.addEventListener("mousemove", MousePosTracker, false); window.addEventListener("dragover", MousePosTracker, false); Services.obs.notifyObservers(window, "browser-delayed-startup-finished", ""); + TelemetryTimestamps.add("delayedStartupFinished"); } function BrowserShutdown() { // In certain scenarios it's possible for unload to be fired before onload, // (e.g. if the window is being closed after browser.js loads but before the // load completes). In that case, there's nothing to do here. if (!gStartupRan) return; @@ -3104,30 +3107,23 @@ function FillInHTMLTooltip(tipElement) tipElement = tipElement.parentNode; } var tipNode = document.getElementById("aHTMLTooltip"); tipNode.style.direction = direction; [titleText, XLinkTitleText, SVGTitleText].forEach(function (t) { if (t && /\S/.test(t)) { - - // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be - // replaced with spaces, and LFs should be removed entirely. - // XXX Bug 322270: We don't preserve the result of entities like , - // which should result in a line break in the tooltip, because we can't - // distinguish that from a literal character in the source by this point. - t = t.replace(/[\r\t]/g, ' '); - t = t.replace(/\n/g, ''); + // Make CRLF and CR render one line break each. + t = t.replace(/\r\n?/g, '\n'); tipNode.setAttribute("label", t); retVal = true; } }); - return retVal; } var browserDragAndDrop = { canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true), dragOver: function (aEvent) { @@ -5196,17 +5192,17 @@ nsBrowserAccess.prototype = { // Pass all params to openDialog to ensure that "url" isn't passed through // loadOneOrMoreURIs, which splits based on "|" newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null); break; case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB : let win, needToFocusWin; // try the current window. if we're in a popup, fall back on the most recent browser window - if (!window.document.documentElement.getAttribute("chromehidden")) + if (window.toolbar.visible) win = window; else { win = Cc["@mozilla.org/browser/browserglue;1"] .getService(Ci.nsIBrowserGlue) .getMostRecentBrowserWindow(); needToFocusWin = true; } @@ -5968,22 +5964,22 @@ function handleDroppedLink(event, url, n }; function MultiplexHandler(event) { try { var node = event.target; var name = node.getAttribute('name'); if (name == 'detectorGroup') { - SetForcedDetector(true); + BrowserCharsetReload(); SelectDetector(event, false); } else if (name == 'charsetGroup') { var charset = node.getAttribute('id'); charset = charset.substring('charset.'.length, charset.length) - SetForcedCharset(charset); + BrowserSetForcedCharacterSet(charset); } else if (name == 'charsetCustomize') { //do nothing - please remove this else statement, once the charset prefs moves to the pref window } else { SetForcedCharset(node.getAttribute('id')); } } catch(ex) { alert(ex); } } @@ -6004,42 +6000,29 @@ function SelectDetector(event, doReload) if (doReload) window.content.location.reload(); } catch (ex) { dump("Failed to set the intl.charset.detector preference.\n"); } } -function SetForcedDetector(doReload) -{ - BrowserSetForcedDetector(doReload); -} - -function SetForcedCharset(charset) -{ - BrowserSetForcedCharacterSet(charset); -} - function BrowserSetForcedCharacterSet(aCharset) { - var docCharset = gBrowser.docShell.QueryInterface(Ci.nsIDocCharset); - docCharset.charset = aCharset; + gBrowser.docShell.charset = aCharset; // Save the forced character-set PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, aCharset); + BrowserCharsetReload(); +} + +function BrowserCharsetReload() +{ BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); } -function BrowserSetForcedDetector(doReload) -{ - gBrowser.documentCharsetInfo.forcedDetector = true; - if (doReload) - BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); -} - function charsetMenuGetElement(parent, id) { return parent.getElementsByAttribute("id", id)[0]; } function UpdateCurrentCharset(target) { // extract the charset from DOM var wnd = document.commandDispatcher.focusedWindow; if ((window == wnd) || (wnd == null)) wnd = window.content; @@ -8917,19 +8900,20 @@ var TabContextMenu = { // Hide "Move to Group" if it's a pinned tab. document.getElementById("context_tabViewMenu").hidden = (this.contextTab.pinned || !TabView.firstUseExperienced); } }; XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () { - Cu.import("resource:///modules/HUDService.jsm"); + let tempScope = {}; + Cu.import("resource:///modules/HUDService.jsm", tempScope); try { - return HUDService.consoleUI; + return tempScope.HUDService.consoleUI; } catch (ex) { Components.utils.reportError(ex); } }); // Prompt user to restart the browser in safe mode function safeModeRestart()
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2370,20 +2370,16 @@ <property name="contentViewerEdit" onget="return this.mCurrentBrowser.contentViewerEdit;" readonly="true"/> <property name="contentViewerFile" onget="return this.mCurrentBrowser.contentViewerFile;" readonly="true"/> - <property name="documentCharsetInfo" - onget="return this.mCurrentBrowser.documentCharsetInfo;" - readonly="true"/> - <property name="contentDocument" onget="return this.mCurrentBrowser.contentDocument;" readonly="true"/> <property name="contentTitle" onget="return this.mCurrentBrowser.contentTitle;" readonly="true"/> @@ -3806,59 +3802,44 @@ this.style.MozUserFocus = ''; </handler> </handlers> </binding> <binding id="tabbrowser-alltabs-popup" extends="chrome://global/content/bindings/popup.xml#popup"> <implementation implements="nsIDOMEventListener"> - <method name="_menuItemOnCommand"> - <parameter name="aEvent"/> - <body><![CDATA[ - gBrowser.selectedTab = aEvent.target.tab; - ]]></body> - </method> - <method name="_tabOnAttrModified"> <parameter name="aEvent"/> <body><![CDATA[ var tab = aEvent.target; - this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab); + if (tab.mCorrespondingMenuitem) + this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab); ]]></body> </method> <method name="_tabOnTabClose"> <parameter name="aEvent"/> <body><![CDATA[ - var menuItem = aEvent.target.mCorrespondingMenuitem; - if (menuItem) - this.removeChild(menuItem); + var tab = aEvent.target; + if (tab.mCorrespondingMenuitem) + this.removeChild(tab.mCorrespondingMenuitem); ]]></body> </method> <method name="handleEvent"> <parameter name="aEvent"/> <body><![CDATA[ - if (!aEvent.isTrusted) - return; - switch (aEvent.type) { - case "command": - this._menuItemOnCommand(aEvent); - break; case "TabAttrModified": this._tabOnAttrModified(aEvent); break; case "TabClose": this._tabOnTabClose(aEvent); break; - case "TabOpen": - this._createTabMenuItem(aEvent.originalTarget); - break; case "scroll": this._updateTabsVisibilityStatus(); break; } ]]></body> </method> <method name="_updateTabsVisibilityStatus"> @@ -3869,18 +3850,16 @@ return; var tabstripBO = tabContainer.mTabstrip.scrollBoxObject; for (var i = 0; i < this.childNodes.length; i++) { let curTab = this.childNodes[i].tab; if (!curTab) // "Tab Groups" menuitem and its menuseparator continue; let curTabBO = curTab.boxObject; - if (!curTabBO) // "Tabs From Other Computers" menuitem - continue; if (curTabBO.screenX >= tabstripBO.screenX && curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width) this.childNodes[i].setAttribute("tabIsVisible", "true"); else this.childNodes[i].removeAttribute("tabIsVisible"); } ]]></body> </method> @@ -3891,24 +3870,20 @@ var menuItem = document.createElementNS( "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem"); menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon"); this._setMenuitemAttributes(menuItem, aTab); - // Keep some attributes of the menuitem in sync with its - // corresponding tab (e.g. the tab label) aTab.mCorrespondingMenuitem = menuItem; menuItem.tab = aTab; - menuItem.addEventListener("command", this, false); this.appendChild(menuItem); - return menuItem; ]]></body> </method> <method name="_setMenuitemAttributes"> <parameter name="aMenuitem"/> <parameter name="aTab"/> <body><![CDATA[ aMenuitem.setAttribute("label", aTab.label); @@ -3933,46 +3908,43 @@ aMenuitem.removeAttribute("selected"); ]]></body> </method> </implementation> <handlers> <handler event="popupshowing"> <![CDATA[ - // set up the menu popup var tabcontainer = gBrowser.tabContainer; - let tabs = gBrowser.visibleTabs; // Listen for changes in the tab bar. - tabcontainer.addEventListener("TabOpen", this, false); tabcontainer.addEventListener("TabAttrModified", this, false); tabcontainer.addEventListener("TabClose", this, false); tabcontainer.mTabstrip.addEventListener("scroll", this, false); + let tabs = gBrowser.visibleTabs; for (var i = 0; i < tabs.length; i++) { - this._createTabMenuItem(tabs[i]); + if (!tabs[i].pinned) + this._createTabMenuItem(tabs[i]); } this._updateTabsVisibilityStatus(); ]]></handler> <handler event="popuphidden"> <![CDATA[ // clear out the menu popup and remove the listeners for (let i = this.childNodes.length - 1; i > 0; i--) { let menuItem = this.childNodes[i]; if (menuItem.tab) { - menuItem.removeEventListener("command", this, false); menuItem.tab.mCorrespondingMenuitem = null; this.removeChild(menuItem); } } var tabcontainer = gBrowser.tabContainer; tabcontainer.mTabstrip.removeEventListener("scroll", this, false); - tabcontainer.removeEventListener("TabOpen", this, false); tabcontainer.removeEventListener("TabAttrModified", this, false); tabcontainer.removeEventListener("TabClose", this, false); ]]></handler> <handler event="DOMMenuItemActive"> <![CDATA[ var tab = event.target.tab; if (tab) { @@ -3983,16 +3955,21 @@ } ]]></handler> <handler event="DOMMenuItemInactive"> <![CDATA[ XULBrowserWindow.setOverLink("", null); ]]></handler> + <handler event="command"><![CDATA[ + if (event.target.tab) + gBrowser.selectedTab = event.target.tab; + ]]></handler> + </handlers> </binding> <binding id="statuspanel" display="xul:hbox"> <content> <xul:hbox class="statuspanel-inner"> <xul:label class="statuspanel-label" role="status"
--- a/browser/base/content/test/browser_bug329212.js +++ b/browser/base/content/test/browser_bug329212.js @@ -6,26 +6,26 @@ function test () { let doc = gBrowser.contentDocument; let tooltip = document.getElementById("aHTMLTooltip"); ok(FillInHTMLTooltip(doc.getElementById("svg1"), "should get title")); is(tooltip.getAttribute("label"), "This is a non-root SVG element title"); ok(FillInHTMLTooltip(doc.getElementById("text1"), "should get title")); - is(tooltip.getAttribute("label"), " This is a title "); + is(tooltip.getAttribute("label"), "\n\n\n This is a title\n\n "); ok(!FillInHTMLTooltip(doc.getElementById("text2"), "should not get title")); ok(!FillInHTMLTooltip(doc.getElementById("text3"), "should not get title")); ok(FillInHTMLTooltip(doc.getElementById("link1"), "should get title")); - is(tooltip.getAttribute("label"), " This is a title "); + is(tooltip.getAttribute("label"), "\n This is a title\n "); ok(FillInHTMLTooltip(doc.getElementById("text4"), "should get title")); - is(tooltip.getAttribute("label"), " This is a title "); + is(tooltip.getAttribute("label"), "\n This is a title\n "); ok(!FillInHTMLTooltip(doc.getElementById("link2"), "should not get title")); ok(FillInHTMLTooltip(doc.getElementById("link3"), "should get title")); ok(tooltip.getAttribute("label") != ""); ok(FillInHTMLTooltip(doc.getElementById("link4"), "should get title")); is(tooltip.getAttribute("label"), "This is an xlink:title attribute");
--- a/browser/base/content/test/browser_clearplugindata.js +++ b/browser/base/content/test/browser_clearplugindata.js @@ -2,18 +2,20 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Test clearing plugin data using sanitize.js. const testURL1 = "http://mochi.test:8888/browser/browser/base/content/test/browser_clearplugindata.html"; const testURL2 = "http://mochi.test:8888/browser/browser/base/content/test/browser_clearplugindata_noage.html"; +let tempScope = {}; Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) - .loadSubScript("chrome://browser/content/sanitize.js"); + .loadSubScript("chrome://browser/content/sanitize.js", tempScope); +let Sanitizer = tempScope.Sanitizer; const pluginHostIface = Ci.nsIPluginHost; var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); pluginHost.QueryInterface(pluginHostIface); var pluginTag; var s;
--- a/browser/base/content/test/browser_sanitize-passwordDisabledHosts.js +++ b/browser/base/content/test/browser_sanitize-passwordDisabledHosts.js @@ -1,13 +1,15 @@ // Bug 474792 - Clear "Never remember passwords for this site" when // clearing site-specific settings in Clear Recent History dialog +let tempScope = {}; Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) - .loadSubScript("chrome://browser/content/sanitize.js"); + .loadSubScript("chrome://browser/content/sanitize.js", tempScope); +let Sanitizer = tempScope.Sanitizer; function test() { var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); // Add a disabled host pwmgr.setLoginSavingEnabled("http://example.com", false);
--- a/browser/base/content/test/browser_sanitize-sitepermissions.js +++ b/browser/base/content/test/browser_sanitize-sitepermissions.js @@ -1,12 +1,14 @@ // Bug 380852 - Delete permission manager entries in Clear Recent History +let tempScope = {}; Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) - .loadSubScript("chrome://browser/content/sanitize.js"); + .loadSubScript("chrome://browser/content/sanitize.js", tempScope); +let Sanitizer = tempScope.Sanitizer; function test() { // Add a permission entry var pm = Services.perms; pm.add(makeURI("http://example.com"), "testing", pm.ALLOW_ACTION); // Sanity check
--- a/browser/base/content/test/browser_sanitize-timespans.js +++ b/browser/base/content/test/browser_sanitize-timespans.js @@ -1,17 +1,19 @@ // Bug 453440 - Test the timespan-based logic of the sanitizer code var now_uSec = Date.now() * 1000; const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); const bhist = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIBrowserHistory); const formhist = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2); +let tempScope = {}; Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) - .loadSubScript("chrome://browser/content/sanitize.js"); + .loadSubScript("chrome://browser/content/sanitize.js", tempScope); +let Sanitizer = tempScope.Sanitizer; function test() { var hoursSinceMidnight = new Date().getHours(); var minutesSinceMidnight = hoursSinceMidnight * 60 + new Date().getMinutes(); setupHistory(); setupFormHistory();
--- a/browser/base/content/test/browser_sanitizeDialog.js +++ b/browser/base/content/test/browser_sanitizeDialog.js @@ -45,19 +45,20 @@ * browser/base/content/test/browser_sanitize-timespans.js does that. This * test checks the UI of the dialog and makes sure it's correctly connected to * the sanitize timespan code. * * Some of this code, especially the history creation parts, was taken from * browser/base/content/test/browser_sanitize-timespans.js. */ -Cc["@mozilla.org/moz/jssubscript-loader;1"]. - getService(Ci.mozIJSSubScriptLoader). - loadSubScript("chrome://browser/content/sanitize.js"); +let tempScope = {}; +Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) + .loadSubScript("chrome://browser/content/sanitize.js", tempScope); +let Sanitizer = tempScope.Sanitizer; const dm = Cc["@mozilla.org/download-manager;1"]. getService(Ci.nsIDownloadManager); const formhist = Cc["@mozilla.org/satchel/form-history;1"]. getService(Ci.nsIFormHistory2); // Add tests here. Each is a function that's called by doNextTest(). var gAllTests = [
--- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -51,17 +51,17 @@ function getBrowserURL() return "chrome://browser/content/browser.xul"; } function getTopWin(skipPopups) { // If this is called in a browser window, use that window regardless of // whether it's the frontmost window, since commands can be executed in // background windows (bug 626148). if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" && - (!skipPopups || !top.document.documentElement.getAttribute("chromehidden"))) + (!skipPopups || top.toolbar.visible)) return top; if (skipPopups) { return Components.classes["@mozilla.org/browser/browserglue;1"] .getService(Components.interfaces.nsIBrowserGlue) .getMostRecentBrowserWindow(); } return Services.wm.getMostRecentWindow("navigator:browser"); @@ -201,17 +201,17 @@ function openLinkIn(url, where, params) saveURL(url, null, null, true, null, aReferrerURI); return; } const Cc = Components.classes; const Ci = Components.interfaces; var w = getTopWin(); if ((where == "tab" || where == "tabshifted") && - w && w.document.documentElement.getAttribute("chromehidden")) { + w && !w.toolbar.visible) { w = getTopWin(true); aRelatedToCurrent = false; } if (!w || where == "window") { var sa = Cc["@mozilla.org/supports-array;1"]. createInstance(Ci.nsISupportsArray);
index 85eb545601f66f0aada8d7d02144ae7627ef53aa..c09d6c1344f7b281220ca2e9c3a55b15e97c8472 GIT binary patch literal 15364 zc%1E-&2Jk;6u{p&ZfqxRviWLBQ_4b=CWzD@P0}C>DUK6pB3dQFX_}}B>Du1d+t|C- z?mB4+0i#Mt91sVPP?5M4i4#Z&^}+!aR1VFFONHox@)r<=0}=-$^JaIP*p5kPL8J=v zlV;!S%$v9K-h6n+3jidNO9TN#0C0mX1us;^&C(7WOg3O5*dDYPHG?<d>XtYh4u`|x z>^sC3?)v@h28Y9W459mj$Yhwwye$c<cQENJX(KQxG8twvZ%e}T985ZybTcV1DKZ&m zGH*+^X%Tk#AneJ6oluCtq{w9Wp<L#0{#V#%cX2$e#bfER^ZSRG+=W6cY-#u+FP|M{ z8s5pc8Ns!G3!re@t{oqlo``8$n%K9o6(h@(zldeiieUx<KM+Ta^H8nJjd<rSDr!Po ziD)@BVO)x)w3VomkYkcQZ{<zN%o!0$w`wLOGZka`DSQUz=a}|IC1Iu(g!a|dzB8wL z#b96{u+}TC_6G-g#lGO++8TYB@U6S;8Q%+IQ_|WSZ|2{!{Vwh!aJV8M*KH3LSCqh3 zKo-aq@^+0!iL1*@%hFg*87WQ2n{K;Ai6=GIhVDjAmaLlPVvUITe#onuD#tW4V_7R| z0wjQJwO|o+zFUmo&q8A{hMu~5@8O0<p{cp0we85!C)$s79B)6dAhg7!_)=1*VI9%Z znjT5#<QXNdsZr%Mc|I-0<n)9Z(M(gz%$V|3lNK)s%@wU)krS_O7foHuqE2gt&RJcS z)k{fTNlYv*8nQW4lAoQ!z^sg$a%Nmz)X@I2BCkwjO+{0UX<0W=&Puv2sYy9<t<3az zMoP-VX+<&?ghtB<_I*;9sR8Q&(X13#)MV$-racgy;mdZ$(;4nzMa4S7v8`4d-mQ#V zfGIi%6HSiq=sjNXC#7~cr)#=Yb*ZfX@}MyG92^30bvJBx*_Tz5RrPnrb$Wf{tNG$x z0l~TGs8Fi+Z9j)jM4VRZ8%Kw*rpeC#`Z2KIP**qh|7pAXp^|2y%APjB2~07NAP!5A z1RXTUfr?Yl2U(O!XvF$j7{*dsr=w;wbofhb`YUa=K%;+JOXo7O;V(w8m87T(ctx+g z=jpJSnvoL6Mk}1Wd;iKB#ubO;+y`}bYhNPsC^1Do`Teu?jXOV;$JHZN27gpGmP{?n zVtV)f&9Ap(`p1)2Oue)}nBD<+zYBdhRq8_Mf`RimWSDX`C95H3E>zn41~>wBWl{8D zj5AhzkD>MR)(9vVaTDteNZ}jL7>r`9Ma#IK8TT{e{u1LYK>Pr0*sBKWA5HGg@GSGr z4bQ?5+M0$1w0#{uf=}T~xDEH<0sKZ>q=g(OUF0P3ljli@jF5BW0+}W=WZv23JZYP7 z6t~Bx$^+;0LvedD?adFoc1c!=oA!o9e;2Ilp6lt>_y6vCdhb;ExF4|fG(%m$D|+RM znpl{WtHyP+BWkTD$35{MuP4Wk`Spz(pO?pVul2OPxpoJ3t|#mdrh0(abLc}4b7AkL zwA)G&zy(oj1<~>($d9^y=<GndRmSnZf0+pe27|rgz`z;$GBY?B=oS0>@MXq>2;V&H z%S>hDB%GDm^7lQU@+Kw^i$oK=ggcXt(OkrKSO12EIJHnH*z<$K;cz&QH8r*n>Hq&P z{`L2N4u^AqA<oh0Xau%C2=0I$qeM)Xiw$7+4*?GQ53n9q7J%(`VJ7po+^3HL{QS?) b|NQ*V;cz${4u`|xa5x+ehr{7;4k&*D(Qxb<
index ea1c05db0dc9a467a75998c7673ce327a8b01971..360fd08066b0a0f695363cabafc776db28210f48 GIT binary patch literal 15364 zc%1E7-%le&9RF@vY_UC9ehD0xbA{k>9%+lfku(}=4?T$9ffhN)amQ`jrQKt@+w5-X zAyy&A2NU1U_~wK72dIhhMU5I?z{L1sjQ8M=H=oX&F}`W)cXwuKDHsmCh~CX7nSN$> zX1+V$&&+prrV9XEET8HJ5COmkgoOa?3Li~7;Gx`riNp4wCu}o#F|xZQ1_lNO1_p*h z2S>=~KiqCGFfjZFVXqG&<tXJ<!W=F4Q06Pz2$V(2QOc`?Ia<#{nWyZdEKn9HM=7ro zri&Iw4<8(TGDlA+oIqKm9Nm}83=H=a$k|;SSM_8<-FAL|6O*r0N<^I+!PwJhM`?|~ zWYUUZ?0*bU`jnK8pP8OW=(@_0%S6G<(aP^7a;jol;qbSdr-rY+&+9`3<}N5&N-xCp zyp}RA#xr^$uB7CIWL$FcmSpA4m}EF5laiH5(EJoW1M_pV_5~$nW#)yBm6d_hkM@cE z;h}J`Ph1)7AL<ha`iF}}`(eW8zSgebV`EcN@uinn*NETlIs!+k1bKycuuxFKTY}7U zSGiYf>Xf9mL~A)U%z~<GX}o&Ny|a`tGrEBn50g7eGObf9_WH!r<diJAB_ierVO7&L zIiXuw$G%$=AO+l33#uu9-PQC*p}|$P?tXvZNPUCQ*woz8`aoNI$I;GX9mnT|=7f}7 zOdGb)XLMCJVrpKVQIfhAS6-0kg_d&hcvecwPpFDy&RFs_i<CE2HGfV{T`u36TFz~^ z;nX>6$g*}ZZ78XUg#}Z#W-9WtbEr^Z+>*27+JcVumlU}$k+T$CGpA+4M4pulL(<Z6 zZ26KZC1iC%i|Ll7XXk|mr!Dq<(va<Tj@@`pN-A2qd*l`wn(pWol9_1qd}%ZO;>*H& zUP)&xb$PcS*u42eY3vl#VyzSPWoLppXLR=qZ|~;KpWb(`N(h`~o~lazVEH-r6vOGU z`AeG}ca0?9{wvV8qxS^%zl(N1Qqe9_)zx}9jwt~WBw-QKV1N#J&~OR`A%~pCXcxc> z=Rv`I1}sov8JeIoRADq!?SC@}p=n*sXJs?wYS_Z5P86uNvVn8<09d-25ywWW+}yZv zwTQ~%^nd0JKW!aMWe*}N!1}_oI8uM6<Rv4OG8c)mbz-&rqq4bZ={c(E#*Ot)%Br?K z?x-5D`-AB<fa^WzL$K0?$nz#n#E5Chxs0qusJTdG@9Uu*{8c&zQRl3q@lmvY&KU&- zRkv^yOspls7>uIY4>{&T)O?7V4{Z~t8MqKYEB373_R&t=(O%-;)<hRP<vH&<zm@R` zFlDbOb-wQCta}em!U*<a9(z-S*Wn#_7e0aw_#S?S-#IVW%pK!;YJRKvgRkYiyuf$x zU3@p+!#~2G;`@jRkGpI>xLtI92;4==B1<3y$eo;$w8*k$zb~BMJ)`l1ukUO$ez@jd z-KEiZ*>(bdZDr$@Zs0<`tFpH`<4!bsz7hX%G<v>U!;$)Gf6=&ktM~=%j7IDaCO^Q7 zdGz5Vb>TJwagRu}^^H3bXy7=cK|Xl=*|P*K?=qhM{${5?Jlx+W4h@~QH#@__;XZM2 z05>~z2;sB+Zg!gBDOAruZ5Gg@Ydf@*aJJ^Q?+@V$rngHaGUkplx6?|(v~JIdQYjfd z(41pnU|?Xl6R061+W-GQ`S;)d85kJuVsQLud^84I9|U*6J}!wswtWD5e+cl9zq@+r zy)cy9MJcZmKBSKU?EKHp|CP80&;JLIl7WGNfq{X6fq{X6fq{X6fq{YHe}caNn2h^T
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -169,17 +169,17 @@ BrowserGlue.prototype = { case "final-ui-startup": this._onProfileStartup(); break; case "browser-delayed-startup-finished": this._onFirstWindowLoaded(); Services.obs.removeObserver(this, "browser-delayed-startup-finished"); break; case "sessionstore-windows-restored": - this._onBrowserStartup(); + this._onWindowsRestored(); break; case "browser:purge-session-history": // reset the console service's error buffer Services.console.logStringMessage(null); // clear the console (in case it's open) Services.console.reset(); break; case "quit-application-requested": this._onQuitRequest(subject, data); @@ -368,18 +368,18 @@ BrowserGlue.prototype = { }, // profile shutdown handler (contains profile cleanup routines) _onProfileShutdown: function BG__onProfileShutdown() { this._shutdownPlaces(); this._sanitizer.onShutdown(); }, - // Browser startup complete. All initial windows have opened. - _onBrowserStartup: function BG__onBrowserStartup() { + // All initial windows have opened. + _onWindowsRestored: function BG__onWindowsRestored() { // Show about:rights notification, if needed. if (this._shouldShowRights()) { this._showRightsNotification(); #ifdef MOZ_TELEMETRY_REPORTING } else { // Only show telemetry notification when about:rights notification is not shown. this._showTelemetryNotification(); #endif @@ -1437,17 +1437,17 @@ BrowserGlue.prototype = { #ifndef XP_WIN #define BROKEN_WM_Z_ORDER #endif // this returns the most recent non-popup browser window getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() { function isFullBrowserWindow(win) { return !win.closed && - !win.document.documentElement.getAttribute("chromehidden"); + win.toolbar.visible; } #ifdef BROKEN_WM_Z_ORDER var win = Services.wm.getMostRecentWindow("navigator:browser"); // if we're lucky, this isn't a popup, and we can just return this if (win && !isFullBrowserWindow(win)) { win = null;
--- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -125,16 +125,18 @@ const TAB_EVENTS = ["TabOpen", "TabClose #define BROKEN_WM_Z_ORDER #endif Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); // debug.js adds NS_ASSERT. cf. bug 669196 Cu.import("resource://gre/modules/debug.js"); +Cu.import("resource:///modules/TelemetryTimestamps.jsm"); + XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { Cu.import("resource://gre/modules/NetUtil.jsm"); return NetUtil; }); XPCOMUtils.defineLazyGetter(this, "ScratchpadManager", function() { Cu.import("resource:///modules/devtools/scratchpad-manager.jsm"); return ScratchpadManager; @@ -289,16 +291,17 @@ SessionStoreService.prototype = { }, /* ........ Global Event Handlers .............. */ /** * Initialize the component */ initService: function() { + TelemetryTimestamps.add("sessionRestoreInitialized"); OBSERVING.forEach(function(aTopic) { Services.obs.addObserver(this, aTopic, true); }, this); var pbs = Cc["@mozilla.org/privatebrowsing;1"]. getService(Ci.nsIPrivateBrowsingService); this._inPrivateBrowsing = pbs.privateBrowsingEnabled; @@ -828,24 +831,25 @@ SessionStoreService.prototype = { // and create its internal data object this._internalWindows[aWindow.__SSi] = { hosts: {} } if (!this._isWindowLoaded(aWindow)) this._windows[aWindow.__SSi]._restoring = true; if (!aWindow.toolbar.visible) this._windows[aWindow.__SSi].isPopup = true; - + // perform additional initialization when the first window is loading if (this._loadState == STATE_STOPPED) { this._loadState = STATE_RUNNING; this._lastSaveTime = Date.now(); // restore a crashed session resp. resume the last session if requested if (this._initialState) { + TelemetryTimestamps.add("sessionRestoreRestoring"); // make sure that the restored tabs are first in the window this._initialState._firstTabs = true; this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0; this.restoreWindow(aWindow, this._initialState, this._isCmdLineEmpty(aWindow, this._initialState)); delete this._initialState; // _loadState changed from "stopped" to "running"
--- a/browser/components/sessionstore/test/browser_248970_b.js +++ b/browser/components/sessionstore/test/browser_248970_b.js @@ -43,17 +43,17 @@ function test() { return aLambda() || true; } catch(ex) { } return false; } var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("TmpD", Components.interfaces.nsIFile); - filePath = file.path; + var filePath = file.path; let fieldList = { "//input[@name='input']": Date.now().toString(), "//input[@name='spaced 1']": Math.random().toString(), "//input[3]": "three", "//input[@type='checkbox']": true, "//input[@name='uncheck']": false, "//input[@type='radio'][1]": false,
--- a/browser/components/sessionstore/test/browser_346337.js +++ b/browser/components/sessionstore/test/browser_346337.js @@ -37,23 +37,23 @@ function test() { /** Test for Bug 346337 **/ var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("TmpD", Components.interfaces.nsILocalFile); file.append("346337_test1.file"); file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); - filePath1 = file.path; + var filePath1 = file.path; file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("TmpD", Components.interfaces.nsILocalFile); file.append("346337_test2.file"); file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); - filePath2 = file.path; + var filePath2 = file.path; let fieldList = { "//input[@name='input']": Date.now().toString(), "//input[@name='spaced 1']": Math.random().toString(), "//input[3]": "three", "//input[@type='checkbox']": true, "//input[@name='uncheck']": false, "//input[@type='radio'][1]": false,
--- a/browser/devtools/highlighter/TreePanel.jsm +++ b/browser/devtools/highlighter/TreePanel.jsm @@ -136,17 +136,17 @@ TreePanel.prototype = { this.treePanelDiv = this.treeBrowserDocument.createElement("div"); this.treeBrowserDocument.body.appendChild(this.treePanelDiv); this.treePanelDiv.ownerPanel = this; this.ioBox = new InsideOutBox(this, this.treePanelDiv); this.ioBox.createObjectBox(this.IUI.win.document.documentElement); this.treeLoaded = true; this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false); this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false); - this.treeIFrame.addEventListener("keypress", this.IUI, false); + this.treeIFrame.focus(); delete this.initializingTreePanel; Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null); if (this.IUI.selection) this.select(this.IUI.selection, true); }, /** @@ -228,17 +228,17 @@ TreePanel.prototype = { treeBox.id = "inspector-tree-box"; treeBox.state = "open"; // for the registerTools API. try { treeBox.height = Services.prefs.getIntPref("devtools.inspector.htmlHeight"); } catch(e) { treeBox.height = 112; } - + treeBox.minHeight = 64; treeBox.flex = 1; toolbarParent.insertBefore(treeBox, toolbar); this.IUI.toolbar.setAttribute("treepanel-open", "true"); treeBox.appendChild(this.treeIFrame); @@ -461,19 +461,16 @@ TreePanel.prototype = { // position the editor editor.style.left = editorLeft + "px"; editor.style.top = editorTop + "px"; // set and select the text editorInput.value = aAttrVal; editorInput.select(); - // remove tree key navigation events - this.treeIFrame.removeEventListener("keypress", this.IUI, false); - // listen for editor specific events this.bindEditorEvent(editor, "click", function(aEvent) { aEvent.stopPropagation(); }); this.bindEditorEvent(editor, "dblclick", function(aEvent) { aEvent.stopPropagation(); }); this.bindEditorEvent(editor, "keypress", @@ -556,19 +553,16 @@ TreePanel.prototype = { this.unbindEditorEvent(editor, "keypress"); // clean up after the editor editorInput.value = ""; editorInput.blur(); this.editingContext = null; this.editingEvents = {}; - // re-add navigation listener - this.treeIFrame.addEventListener("keypress", this.IUI, false); - // event notification Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, null); }, /** * Commit the edits made in the editor, then close it. */ @@ -695,17 +689,16 @@ TreePanel.prototype = { this.treePanelDiv.ownerPanel = null; let parent = this.treePanelDiv.parentNode; parent.removeChild(this.treePanelDiv); delete this.treePanelDiv; delete this.treeBrowserDocument; } if (this.treeIFrame) { - this.treeIFrame.removeEventListener("keypress", this.IUI, false); this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false); this.treeIFrame.removeEventListener("click", this.onTreeClick, false); let parent = this.treeIFrame.parentNode; parent.removeChild(this.treeIFrame); delete this.treeIFrame; } if (this.ioBox) {
--- a/browser/devtools/highlighter/highlighter.jsm +++ b/browser/devtools/highlighter/highlighter.jsm @@ -693,22 +693,24 @@ Highlighter.prototype = { this.browser.removeEventListener("mousedown", this, true); this.browser.removeEventListener("mouseup", this, true); }, attachPageListeners: function Highlighter_attachPageListeners() { this.browser.addEventListener("resize", this, true); this.browser.addEventListener("scroll", this, true); + this.browser.addEventListener("MozAfterPaint", this, true); }, detachPageListeners: function Highlighter_detachPageListeners() { this.browser.removeEventListener("resize", this, true); this.browser.removeEventListener("scroll", this, true); + this.browser.removeEventListener("MozAfterPaint", this, true); }, attachKeysListeners: function Highlighter_attachKeysListeners() { this.browser.addEventListener("keypress", this, true); this.highlighterContainer.addEventListener("keypress", this, true); }, @@ -729,98 +731,36 @@ Highlighter.prototype = { switch (aEvent.type) { case "click": this.handleClick(aEvent); break; case "mousemove": this.handleMouseMove(aEvent); break; case "resize": + this.computeZoomFactor(); + break; + case "MozAfterPaint": case "scroll": - this.computeZoomFactor(); this.brieflyDisableTransitions(); this.invalidateSize(); break; case "dblclick": case "mousedown": case "mouseup": aEvent.stopPropagation(); aEvent.preventDefault(); break; - break; case "keypress": switch (aEvent.keyCode) { case this.chromeWin.KeyEvent.DOM_VK_RETURN: this.locked ? this.unlock() : this.lock(); aEvent.preventDefault(); aEvent.stopPropagation(); break; - case this.chromeWin.KeyEvent.DOM_VK_LEFT: - let node; - if (this.node) { - node = this.node.parentNode; - } else { - node = this.defaultSelection; - } - if (node && this.isNodeHighlightable(node)) { - this.highlight(node); - } - aEvent.preventDefault(); - aEvent.stopPropagation(); - break; - case this.chromeWin.KeyEvent.DOM_VK_RIGHT: - if (this.node) { - // Find the first child that is highlightable. - for (let i = 0; i < this.node.childNodes.length; i++) { - node = this.node.childNodes[i]; - if (node && this.isNodeHighlightable(node)) { - break; - } - } - } else { - node = this.defaultSelection; - } - if (node && this.isNodeHighlightable(node)) { - this.highlight(node, true); - } - aEvent.preventDefault(); - aEvent.stopPropagation(); - break; - case this.chromeWin.KeyEvent.DOM_VK_UP: - if (this.node) { - // Find a previous sibling that is highlightable. - node = this.node.previousSibling; - while (node && !this.isNodeHighlightable(node)) { - node = node.previousSibling; - } - } else { - node = this.defaultSelection; - } - if (node && this.isNodeHighlightable(node)) { - this.highlight(node, true); - } - aEvent.preventDefault(); - aEvent.stopPropagation(); - break; - case this.chromeWin.KeyEvent.DOM_VK_DOWN: - if (this.node) { - // Find a next sibling that is highlightable. - node = this.node.nextSibling; - while (node && !this.isNodeHighlightable(node)) { - node = node.nextSibling; - } - } else { - node = this.defaultSelection; - } - if (node && this.isNodeHighlightable(node)) { - this.highlight(node, true); - } - aEvent.preventDefault(); - aEvent.stopPropagation(); - break; } } }, /** * Disable the CSS transitions for a short time to avoid laggy animations * during scrolling or resizing. */
--- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -281,16 +281,18 @@ InspectorUI.prototype = { this.isDirty = false; this.progressListener = new InspectorProgressListener(this); this.chromeWin.addEventListener("keypress", this, false); // initialize the highlighter this.highlighter = new Highlighter(this.chromeWin); + + this.setupNavigationKeys(); this.highlighterReady(); }, /** * Register the Rule View in the Sidebar. */ registerRuleView: function IUI_registerRuleView() { @@ -345,16 +347,46 @@ InspectorUI.prototype = { this.store.setValue(this.winID, "selectedNode", null); this.store.setValue(this.winID, "inspecting", true); this.store.setValue(this.winID, "isDirty", this.isDirty); this.win.addEventListener("pagehide", this, true); } }, /** + * Browse nodes according to the breadcrumbs layout, only for some specific + * elements of the UI. + */ + setupNavigationKeys: function IUI_setupNavigationKeys() + { + // UI elements that are arrow keys sensitive: + // - highlighter veil; + // - content window (when the highlighter `veil is pointer-events:none`; + // - the Inspector toolbar. + + this.onKeypress = this.onKeypress.bind(this); + + this.highlighter.highlighterContainer.addEventListener("keypress", + this.onKeypress, true); + this.win.addEventListener("keypress", this.onKeypress, true); + this.toolbar.addEventListener("keypress", this.onKeypress, true); + }, + + /** + * Remove the event listeners for the arrowkeys. + */ + removeNavigationKeys: function IUI_removeNavigationKeys() + { + this.highlighter.highlighterContainer.removeEventListener("keypress", + this.onKeypress, true); + this.win.removeEventListener("keypress", this.onKeypress, true); + this.toolbar.removeEventListener("keypress", this.onKeypress, true); + }, + + /** * Close inspector UI and associated panels. Unhighlight and stop inspecting. * Remove event listeners for document scrolling, resize, * tabContainer.TabSelect and others. * * @param boolean aKeepStore * Tells if you want the store associated to the current tab/window to * be cleared or not. Set this to true to not clear the store, or false * otherwise. @@ -370,16 +402,18 @@ InspectorUI.prototype = { return; } let winId = new String(this.winID); // retain this to notify observers. this.closing = true; this.toolbar.hidden = true; + this.removeNavigationKeys(); + this.progressListener.destroy(); delete this.progressListener; if (!aKeepStore) { this.store.deleteStore(this.winID); this.win.removeEventListener("pagehide", this, true); } else { // Update the store before closing. @@ -587,16 +621,24 @@ InspectorUI.prototype = { } } if (this.store.isEmpty()) { this.tabbrowser.tabContainer.removeEventListener("TabSelect", this, false); } break; + case "keypress": + switch (event.keyCode) { + case this.chromeWin.KeyEvent.DOM_VK_ESCAPE: + this.closeInspectorUI(false); + event.preventDefault(); + event.stopPropagation(); + break; + } case "pagehide": win = event.originalTarget.defaultView; // Skip iframes/frames. if (!win || win.frameElement || win.top != win) { break; } win.removeEventListener(event.type, this, true); @@ -606,28 +648,76 @@ InspectorUI.prototype = { this.store.deleteStore(winID); } if (this.store.isEmpty()) { this.tabbrowser.tabContainer.removeEventListener("TabSelect", this, false); } break; - case "keypress": - switch (event.keyCode) { - case this.chromeWin.KeyEvent.DOM_VK_ESCAPE: - this.closeInspectorUI(false); - event.preventDefault(); - event.stopPropagation(); - break; + } + }, + + /* + * handles "keypress" events. + */ + onKeypress: function IUI_onKeypress(event) + { + let node = null; + let bc = this.breadcrumbs; + switch (event.keyCode) { + case this.chromeWin.KeyEvent.DOM_VK_LEFT: + if (bc.currentIndex != 0) + node = bc.nodeHierarchy[bc.currentIndex - 1].node; + if (node && this.highlighter.isNodeHighlightable(node)) + this.highlighter.highlight(node); + event.preventDefault(); + event.stopPropagation(); + break; + case this.chromeWin.KeyEvent.DOM_VK_RIGHT: + if (bc.currentIndex < bc.nodeHierarchy.length - 1) + node = bc.nodeHierarchy[bc.currentIndex + 1].node; + if (node && this.highlighter.isNodeHighlightable(node)) { + this.highlighter.highlight(node); } + event.preventDefault(); + event.stopPropagation(); + break; + case this.chromeWin.KeyEvent.DOM_VK_UP: + if (this.selection) { + // Find a previous sibling that is highlightable. + node = this.selection.previousSibling; + while (node && !this.highlighter.isNodeHighlightable(node)) { + node = node.previousSibling; + } + } + if (node && this.highlighter.isNodeHighlightable(node)) { + this.highlighter.highlight(node, true); + } + event.preventDefault(); + event.stopPropagation(); + break; + case this.chromeWin.KeyEvent.DOM_VK_DOWN: + if (this.selection) { + // Find a next sibling that is highlightable. + node = this.selection.nextSibling; + while (node && !this.highlighter.isNodeHighlightable(node)) { + node = node.nextSibling; + } + } + if (node && this.highlighter.isNodeHighlightable(node)) { + this.highlighter.highlight(node, true); + } + event.preventDefault(); + event.stopPropagation(); break; } }, + ///////////////////////////////////////////////////////////////////////// //// CssRuleView methods /** * Is the cssRuleView open? */ isRuleViewOpen: function IUI_isRuleViewOpen() { @@ -1715,16 +1805,18 @@ HTMLBreadcrumbs.prototype = { setCursor: function BC_setCursor(aIdx) { // Unselect the previously selected button if (this.currentIndex > -1 && this.currentIndex < this.nodeHierarchy.length) { this.nodeHierarchy[this.currentIndex].button.removeAttribute("checked"); } if (aIdx > -1) { this.nodeHierarchy[aIdx].button.setAttribute("checked", "true"); + if (this.hadFocus) + this.nodeHierarchy[aIdx].button.focus(); } this.currentIndex = aIdx; }, /** * Get the index of the node in the cache. * * @param aNode @@ -1890,16 +1982,20 @@ HTMLBreadcrumbs.prototype = { /** * Update the breadcrumbs display when a new node is selected. */ update: function BC_update() { this.menu.hidePopup(); + let cmdDispatcher = this.IUI.chromeDoc.commandDispatcher; + this.hadFocus = (cmdDispatcher.focusedElement && + cmdDispatcher.focusedElement.parentNode == this.container); + let selection = this.IUI.selection; let idx = this.indexOf(selection); // Is the node already displayed in the breadcrumbs? if (idx > -1) { // Yes. We select it. this.setCursor(idx); } else { @@ -1919,17 +2015,18 @@ HTMLBreadcrumbs.prototype = { idx = this.indexOf(selection); this.setCursor(idx); } // Add the first child of the very last node of the breadcrumbs if possible. this.ensureFirstChild(); // Make sure the selected node and its neighbours are visible. this.scroll(); - } + }, + } ///////////////////////////////////////////////////////////////////////// //// Initializers XPCOMUtils.defineLazyGetter(InspectorUI.prototype, "strings", function () { return Services.strings.createBundle(
--- a/browser/devtools/highlighter/test/Makefile.in +++ b/browser/devtools/highlighter/test/Makefile.in @@ -64,16 +64,17 @@ include $(topsrcdir)/config/rules.mk browser_inspector_bug_672902_keyboard_shortcuts.js \ browser_inspector_keybindings.js \ browser_inspector_breadcrumbs.html \ browser_inspector_breadcrumbs.js \ browser_inspector_bug_699308_iframe_navigation.js \ browser_inspector_changes.js \ browser_inspector_ruleviewstore.js \ browser_inspector_duplicate_ruleview.js \ + browser_inspector_invalidate.js \ head.js \ $(NULL) # Disabled due to constant failures # browser_inspector_treePanel_click.js \ libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/browser/devtools/highlighter/test/browser_inspector_invalidate.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +let doc; +let div; + +function createDocument() +{ + div = doc.createElement("div"); + div.setAttribute("style", "width: 100px; height: 100px;"); + doc.body.appendChild(div); + + Services.obs.addObserver(runTest, + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.toggleInspectorUI(); +} + +function runTest(subject) +{ + Services.obs.removeObserver(runTest, + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + + InspectorUI.highlighter.highlight(div); + + executeSoon(function() { + let veilBoxDims = InspectorUI.highlighter.veilTransparentBox; + is(veilBoxDims.style.width, "100px", "selection has the right width"); + + div.style.width = "200px"; + setTimeout(function () { + let veilBoxDims = InspectorUI.highlighter.veilTransparentBox; + is(veilBoxDims.style.width, "200px", "selection updated"); + InspectorUI.closeInspectorUI(); + gBrowser.removeCurrentTab(); + finish(); + }, 1000); + }); +} + +function test() +{ + waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function() { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + doc = content.document; + waitForFocus(createDocument, content); + }, true); + + content.location = "data:text/html,basic tests for inspector"; +} +
--- a/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js +++ b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js @@ -87,18 +87,17 @@ function inspectorUIOpen1() function ruleViewOpened1() { let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0]; is(prop.name, "background-color", "First prop is the background color prop."); prop.setEnabled(false); // Open second tab and switch to it - tab2 = gBrowser.addTab(); - gBrowser.selectedTab = tab2; + gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function(evt) { gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true); waitForFocus(inspectorTabOpen2, content); }, true); content.location = "data:text/html,<p>tab 2: the inspector should close now"; }
--- a/browser/devtools/highlighter/test/head.js +++ b/browser/devtools/highlighter/test/head.js @@ -32,17 +32,19 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const Cu = Components.utils; -Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope); +let LayoutHelpers = tempScope.LayoutHelpers; function isHighlighting() { let veil = InspectorUI.highlighter.veilTransparentBox; return !(veil.style.visibility == "hidden"); } function getHighlitNode()
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js @@ -1,14 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); +let tempScope = {}; +Cu.import("resource://gre/modules/NetUtil.jsm", tempScope); +Cu.import("resource://gre/modules/FileUtils.jsm", tempScope); +let NetUtil = tempScope.NetUtil; +let FileUtils = tempScope.FileUtils; // Reference to the Scratchpad chrome window object. let gScratchpadWindow; // Reference to the Scratchpad object. let gScratchpad; // Reference to the temporary nsIFile we will work with.
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ - -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); +let tempScope = {}; +Cu.import("resource://gre/modules/NetUtil.jsm", tempScope); +Cu.import("resource://gre/modules/FileUtils.jsm", tempScope); +let NetUtil = tempScope.NetUtil; +let FileUtils = tempScope.FileUtils; // only finish() when correct number of tests are done const expected = 5; var count = 0; function done() { if (++count == expected) { cleanup();
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; function test() { waitForExplicitFinish(); gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function onLoad() { gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
--- a/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js @@ -70,16 +70,17 @@ function runTests() ok(sp.getText(), "window.foobarBug636725 = 'aloha2';", "setText() worked"); ok(!window.foobarBug636725, "no window.foobarBug636725"); sp.run(); is(window.foobarBug636725, "aloha2", "window.foobarBug636725 has been set"); + delete window.foobarBug636725; sp.setText("gBrowser", 7); ok(sp.getText(), "window.gBrowser", "setText() worked with no end for the replace range"); is(typeof sp.run()[2].addTab, "function", "chrome context has access to chrome objects");
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js @@ -1,14 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); +let tempScope = {}; +Cu.import("resource://gre/modules/NetUtil.jsm", tempScope); +Cu.import("resource://gre/modules/FileUtils.jsm", tempScope); +let NetUtil = tempScope.NetUtil; +let FileUtils = tempScope.FileUtils; // Reference to the Scratchpad object. let gScratchpad; // Reference to the temporary nsIFile we will work with. let gFile; // The temporary file content.
--- a/browser/devtools/shared/SplitView.jsm +++ b/browser/devtools/shared/SplitView.jsm @@ -64,21 +64,16 @@ function SplitView(aRoot) this._root = aRoot; this._controller = aRoot.querySelector(".splitview-controller"); this._nav = aRoot.querySelector(".splitview-nav"); this._side = aRoot.querySelector(".splitview-side-details"); this._activeSummary = null this._mql = aRoot.ownerDocument.defaultView.matchMedia(LANDSCAPE_MEDIA_QUERY); - this._filter = aRoot.querySelector(".splitview-filter"); - if (this._filter) { - this._setupFilterBox(); - } - // items list focus and search-on-type handling this._nav.addEventListener("keydown", function onKeyCatchAll(aEvent) { function getFocusedItemWithin(nav) { let node = nav.ownerDocument.activeElement; while (node && node.parentNode != nav) { node = node.parentNode; } return node; @@ -111,23 +106,16 @@ function SplitView(aRoot) if (newFocusOrdinal !== undefined) { aEvent.stopPropagation(); let el = this.getSummaryElementByOrdinal(newFocusOrdinal); if (el) { el.focus(); } return false; } - - // search-on-type when any non-whitespace character is pressed while list - // has the focus - if (this._filter && - !/\s/.test(String.fromCharCode(aEvent.which))) { - this._filter.focus(); - } }.bind(this), false); } SplitView.prototype = { /** * Retrieve whether the UI currently has a landscape orientation. * * @return boolean @@ -222,20 +210,16 @@ SplitView.prototype = { * - function(DOMElement summary, DOMElement details, object data) onCreate * Called when the item has been added. * - function(summary, details, data) onShow * Called when the item is shown/active. * - function(summary, details, data) onHide * Called when the item is hidden/inactive. * - function(summary, details, data) onDestroy * Called when the item has been removed. - * - function(summary, details, data, query) onFilterBy - * Called when the user performs a filtering search. - * If the function returns false, the item does not match query - * string and will be hidden. * - object data * Object to pass to the callbacks above. * - number ordinal * Items with a lower ordinal are displayed before those with a * higher ordinal. */ appendItem: function ASV_appendItem(aSummary, aDetails, aOptions) { @@ -323,81 +307,16 @@ SplitView.prototype = { removeAll: function ASV_removeAll() { while (this._nav.hasChildNodes()) { this.removeItem(this._nav.firstChild); } }, /** - * Filter items by given string. - * Matching is performed on every item by calling onFilterBy when defined - * and then by searching aQuery in the summary element's text item. - * Non-matching item is hidden. - * - * If no item matches, 'splitview-all-filtered' class is set on the filter - * input element and the splitview-nav element. - * - * @param string aQuery - * The query string. Use null to reset (no filter). - * @return number - * The number of filtered (non-matching) item. - */ - filterItemsBy: function ASV_filterItemsBy(aQuery) - { - if (!this._nav.hasChildNodes()) { - return 0; - } - if (aQuery) { - aQuery = aQuery.trim(); - } - if (!aQuery) { - for (let i = 0; i < this._nav.childNodes.length; ++i) { - this._nav.childNodes[i].classList.remove("splitview-filtered"); - } - this._filter.classList.remove("splitview-all-filtered"); - this._nav.classList.remove("splitview-all-filtered"); - return 0; - } - - let count = 0; - let filteredCount = 0; - for (let i = 0; i < this._nav.childNodes.length; ++i) { - let summary = this._nav.childNodes[i]; - - let matches = false; - let binding = summary.getUserData(BINDING_USERDATA); - if (binding.onFilterBy) { - matches = binding.onFilterBy(summary, binding._details, binding.data, aQuery); - } - if (!matches) { // try text content - let content = summary.textContent.toUpperCase(); - matches = (content.indexOf(aQuery.toUpperCase()) > -1); - } - - count++; - if (!matches) { - summary.classList.add("splitview-filtered"); - filteredCount++; - } else { - summary.classList.remove("splitview-filtered"); - } - } - - if (count > 0 && filteredCount == count) { - this._filter.classList.add("splitview-all-filtered"); - this._nav.classList.add("splitview-all-filtered"); - } else { - this._filter.classList.remove("splitview-all-filtered"); - this._nav.classList.remove("splitview-all-filtered"); - } - return filteredCount; - }, - - /** * Set the item's CSS class name. * This sets the class on both the summary and details elements, retaining * any SplitView-specific classes. * * @param DOMElement aSummary * Summary element of the item to set. * @param string aClassName * One or more space-separated CSS classes. @@ -410,44 +329,9 @@ SplitView.prototype = { viewSpecific = aSummary.className.match(/(splitview\-[\w-]+)/g); viewSpecific = viewSpecific ? viewSpecific.join(" ") : ""; aSummary.className = viewSpecific + " " + aClassName; viewSpecific = binding._details.className.match(/(splitview\-[\w-]+)/g); viewSpecific = viewSpecific ? viewSpecific.join(" ") : ""; binding._details.className = viewSpecific + " " + aClassName; }, - - /** - * Set up filter search box. - */ - _setupFilterBox: function ASV__setupFilterBox() - { - let clearFilter = function clearFilter(aEvent) { - this._filter.value = ""; - this.filterItemsBy(""); - return false; - }.bind(this); - - this._filter.addEventListener("command", function onFilterInput(aEvent) { - this.filterItemsBy(this._filter.value); - }.bind(this), false); - - this._filter.addEventListener("keyup", function onFilterKeyUp(aEvent) { - if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) { - clearFilter(); - } - if (aEvent.keyCode == aEvent.DOM_VK_ENTER || - aEvent.keyCode == aEvent.DOM_VK_RETURN) { - // autofocus matching item if there is only one - let matches = this._nav.querySelectorAll("* > li:not(.splitview-filtered)"); - if (matches.length == 1) { - this.activeSummary = matches[0]; - } - } - }.bind(this), false); - - let clearButtons = this._root.querySelectorAll(".splitview-filter-clearButton"); - for (let i = 0; i < clearButtons.length; ++i) { - clearButtons[i].addEventListener("click", clearFilter, false); - } - } };
--- a/browser/devtools/shared/splitview.css +++ b/browser/devtools/shared/splitview.css @@ -53,16 +53,17 @@ box, .splitview-controller, .splitview-main { -moz-box-flex: 0; } .splitview-controller { min-height: 3em; max-height: 14em; + max-width: 400px; } .splitview-nav { display: -moz-box; overflow-x: hidden; overflow-y: auto; } @@ -118,9 +119,13 @@ ol.splitview-nav > li.splitview-filtered @media (max-aspect-ratio: 5/3) { #splitview-details-toolbar { display: none; } .splitview-portrait-resizer { display: -moz-box; } + + .splitview-controller { + max-width: none; + } }
--- a/browser/devtools/shared/test/browser_promise_basic.js +++ b/browser/devtools/shared/test/browser_promise_basic.js @@ -1,14 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that our Promise implementation works properly -Cu.import("resource:///modules/devtools/Promise.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/devtools/Promise.jsm", tempScope); +let Promise = tempScope.Promise; function test() { addTab("about:blank", function() { info("Starting Promise Tests"); testBasic(); }); }
--- a/browser/devtools/shared/test/browser_templater_basic.js +++ b/browser/devtools/shared/test/browser_templater_basic.js @@ -1,15 +1,18 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that the DOM Template engine works properly -Cu.import("resource:///modules/devtools/Templater.jsm"); -Cu.import("resource:///modules/devtools/Promise.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/devtools/Templater.jsm", tempScope); +Cu.import("resource:///modules/devtools/Promise.jsm", tempScope); +let template = tempScope.template; +let Promise = tempScope.Promise; function test() { addTab("http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html", function() { info("Starting DOM Templater Tests"); runTest(0); }); }
--- a/browser/devtools/sourceeditor/orion/README +++ b/browser/devtools/sourceeditor/orion/README @@ -10,16 +10,18 @@ To upgrade Orion to a newer version see Orion version: git clone from 2011-12-09 commit hash d8a6dc01d9c561d6eb99f03b64c8c78ce785c59d + patch for Eclipse Bug 366312 - right-clicking outside of the selection causes the caret to move https://github.com/mihaisucan/orion.client/tree/bug-366312 see https://bugs.eclipse.org/bugs/show_bug.cgi?id=366312 + patch for Mozilla Bug 711737 - Orion should support all the CSS properties from CSS1, CSS2, CSS2.1 and CSS3 https://bugzilla.mozilla.org/show_bug.cgi?id=711737 + + patch for Mozilla Bug 719028 - Style Editor does not highlight a few CSS2.0 and CSS3 properties + https://bugzilla.mozilla.org/show_bug.cgi?id=719028 # License The following files are licensed according to the contents in the LICENSE file: orion.js orion.css
--- a/browser/devtools/sourceeditor/orion/orion.js +++ b/browser/devtools/sourceeditor/orion/orion.js @@ -5,16 +5,17 @@ * available under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). * * Contributors: * Felipe Heidrich (IBM Corporation) - initial API and implementation * Silenio Quarti (IBM Corporation) - initial API and implementation * Mihai Sucan (Mozilla Foundation) - fix for Bug#364214 + * Alex Lakatos (Mozilla Contributor) - fix for Mozilla Bug#719028 */ /*global window */ /** * Evaluates the definition function and mixes in the returned module with * the module specified by <code>moduleName</code>. * <p> @@ -10483,23 +10484,24 @@ define(['orion/textview/annotations'], f "border-radius", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-spacing", "border-style", "border-top", "border-top-color", "border-top-left-radius", "border-top-right-radius", "border-top-style", "border-top-width", "border-width", "bottom", "box-align", "box-decoration-break", "box-direction", "box-flex", "box-flex-group", "box-lines", "box-ordinal-group", "box-orient", "box-pack", "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", "caption-side", "clear", "clip", "color", "color-profile", "column-count", "column-fill", "column-gap", "column-rule", "column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-width", "columns", "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after", "cue-before", "cursor", "direction", "display", "dominant-baseline", "drop-initial-after-adjust", "drop-initial-after-align", "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size", - "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "float", "float-offset", "font", "font-family", "font-size", - "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", - "height", "hyphenate-after", "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", - "image-orientation", "image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height", - "line-stacking", "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", - "list-style-position", "list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after", - "mark-before", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height", + "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex-align", "flex-flow", "flex-inline-pack", "flex-order", + "flex-pack", "float", "float-offset", "font", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", + "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", "height", "hyphenate-after", + "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", "image-orientation", + "image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height", "line-stacking", + "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", "list-style-position", + "list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after", "mark-before", + "marker-offset", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height", "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", "nav-up", "opacity", "orphans", "outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "overflow-x", "overflow-y", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "page", "page-break-after", "page-break-before", "page-break-inside", "page-policy", "pause", "pause-after", "pause-before", "perspective", "perspective-origin", "phonemes", "pitch", "pitch-range", "play-during", "position", "presentation-level", "punctuation-trim", "quotes", "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span", "size", "speak", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "table-layout", "target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", "text-emphasis",
--- a/browser/devtools/sourceeditor/test/browser_bug650345_find.js +++ b/browser/devtools/sourceeditor/test/browser_bug650345_find.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { waitForExplicitFinish();
--- a/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js +++ b/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { waitForExplicitFinish();
--- a/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js +++ b/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { waitForExplicitFinish();
--- a/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js +++ b/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { waitForExplicitFinish();
--- a/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js +++ b/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT); if (component != "orion") {
--- a/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js +++ b/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT); if (component == "textarea") {
--- a/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js +++ b/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT); if (component != "orion") {
--- a/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js +++ b/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let editor; function test() { if (Services.appinfo.OS != "Linux") { ok(true, "this test only applies to Linux, skipping.")
--- a/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js +++ b/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js @@ -1,15 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -Cu.import("resource:///modules/source-editor.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; let testWin; let testDoc; let editor; function test() { waitForExplicitFinish();
--- a/browser/devtools/styleeditor/StyleEditor.jsm +++ b/browser/devtools/styleeditor/StyleEditor.jsm @@ -224,16 +224,18 @@ StyleEditor.prototype = { placeholderText: this._state.text, //! this is initialText (bug 680371) showLineNumbers: true, mode: SourceEditor.MODES.CSS, readOnly: this._state.readOnly, keys: this._getKeyBindings() }; sourceEditor.init(aElement, config, function onSourceEditorReady() { + setupBracketCompletion(sourceEditor); + sourceEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, function onTextChanged(aEvent) { this.updateStyleSheet(); }.bind(this)); this._sourceEditor = sourceEditor; if (this._focusOnSourceEditorReady) { @@ -1127,8 +1129,53 @@ function prettifyCSS(aText) * @param string aText * @param number aCount * @return string */ function repeat(aText, aCount) { return (new Array(aCount + 1)).join(aText); } + +/** + * Set up bracket completion on a given SourceEditor. + * This automatically closes the following CSS brackets: "{", "(", "[" + * + * @param SourceEditor aSourceEditor + */ +function setupBracketCompletion(aSourceEditor) +{ + let editorElement = aSourceEditor.editorElement; + let pairs = { + 123: { // { + closeString: "}", + closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET + }, + 40: { // ( + closeString: ")", + closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_0 + }, + 91: { // [ + closeString: "]", + closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET + }, + }; + + editorElement.addEventListener("keypress", function onKeyPress(aEvent) { + let pair = pairs[aEvent.charCode]; + if (!pair) { + return true; + } + + // We detected an open bracket, sending closing character + let keyCode = pair.closeKeyCode; + let charCode = pair.closeString.charCodeAt(0); + let modifiers = 0; + let utils = editorElement.ownerDocument.defaultView. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils); + let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers); + utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled); + utils.sendKeyEvent("keyup", keyCode, 0, modifiers); + // and rewind caret + aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1); + }, false); +}
--- a/browser/devtools/styleeditor/StyleEditorChrome.jsm +++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm @@ -397,17 +397,19 @@ StyleEditorChrome.prototype = { */ _updateSummaryForEditor: function SEC__updateSummaryForEditor(aEditor, aSummary) { let summary = aSummary || this.getSummaryElementForEditor(aEditor); let ruleCount = aEditor.styleSheet.cssRules.length; this._view.setItemClassName(summary, aEditor.flags); - text(summary, ".stylesheet-name", aEditor.getFriendlyName()); + let label = summary.querySelector(".stylesheet-name > label"); + label.setAttribute("value", aEditor.getFriendlyName()); + text(summary, ".stylesheet-title", aEditor.styleSheet.title || ""); text(summary, ".stylesheet-rule-count", PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount)); text(summary, ".stylesheet-error-message", aEditor.errorMessage); }, /** * IStyleEditorActionListener implementation
--- a/browser/devtools/styleeditor/styleeditor.css +++ b/browser/devtools/styleeditor/styleeditor.css @@ -15,16 +15,17 @@ * The Original Code is Style Editor code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Cedric Vivier <cedricv@neonux.com> (original author) + * Paul Rouget <paul@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -59,32 +60,27 @@ li.error > .stylesheet-info > .styleshee -moz-box-flex: 1; } .stylesheet-info > h1 { -moz-box-flex: 1; } .stylesheet-name { - /* clip the text at the beginning */ - display: -moz-box; - direction: rtl; - text-align: left; - overflow: hidden; + outline: none; +} + +.stylesheet-name > label { + cursor: pointer; } .splitview-nav > li > hgroup.stylesheet-info { -moz-box-pack: center; } -.splitview-nav:-moz-locale-dir(ltr) > li.unsaved > hgroup .stylesheet-name:before, -.splitview-nav:-moz-locale-dir(rtl) > li.unsaved > hgroup .stylesheet-name:after { - content: "* "; -} - .stylesheet-enabled { display: -moz-box; } .stylesheet-saveButton { display: none; } @@ -102,17 +98,17 @@ li:hover > hgroup > .stylesheet-more > h @media (max-aspect-ratio: 5/3) { li.splitview-active > hgroup > .stylesheet-more > .stylesheet-rule-count, li:hover > hgroup > .stylesheet-more > .stylesheet-rule-count { display: none; } .stylesheet-more { -moz-box-flex: 1; - -moz-box-direction: reverse; + -moz-box-pack: end; } .splitview-nav > li > hgroup.stylesheet-info { -moz-box-orient: horizontal; -moz-box-flex: 1; } .stylesheet-more > spacer {
--- a/browser/devtools/styleeditor/styleeditor.xul +++ b/browser/devtools/styleeditor/styleeditor.xul @@ -49,84 +49,84 @@ xmlns="http://www.w3.org/1999/xhtml" id="style-editor-chrome-window" title="&window.title;" windowtype="Tools:StyleEditor" width="800" height="280" persist="screenX screenY width height sizemode"> <xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/> +<xul:commandset id="style-editor-commandset"> + <xul:command id="style-editor-cmd-close" oncommand="window.close();"/> +</xul:commandset> + +<xul:keyset id="style-editor-keyset"> + <xul:key id="style-editor-key-close" + key="&closeCmd.key;" + command="style-editor-cmd-close" + modifiers="accel"/> +</xul:keyset> + <xul:box id="style-editor-chrome" class="splitview-root loading"> - <xul:box class="splitview-controller" id="stylesheets-controller" persist="width height"> + <xul:box class="splitview-controller"> <xul:box class="splitview-main"> <xul:toolbar class="devtools-toolbar"> <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton" accesskey="&newButton.accesskey;" tooltiptext="&newButton.tooltip;" label="&newButton.label;" disabled="true"/> <xul:toolbarbutton class="style-editor-importButton devtools-toolbarbutton" accesskey="&importButton.accesskey;" tooltiptext="&importButton.tooltip;" label="&importButton.label;" disabled="true"/> - <xul:spacer flex="1"/> - <xul:textbox class="splitview-filter devtools-searchinput" - type="search" flex="1" - tooltiptext="&searchInput.tooltip;" - placeholder="&searchInput.placeholder;"/> </xul:toolbar> </xul:box> - <xul:box class="splitview-nav-container"> + <xul:box id="splitview-resizer-target" class="splitview-nav-container" + persist="width height"> <ol class="splitview-nav" tabindex="0"></ol> <div class="splitview-nav placeholder empty"> <p><strong>&noStyleSheet.label;</strong></p> <p>&noStyleSheet-tip-start.label; <a href="#" class="style-editor-newButton">&noStyleSheet-tip-action.label;</a> &noStyleSheet-tip-end.label;</p> </div> - <div class="splitview-nav placeholder all-filtered"> - <p><strong>&searchNoResults.label;</strong></p> - <p> - <a href="#" - class="splitview-filter-clearButton">&searchClearButton.label;</a> - </p> - </div> </xul:box> <!-- .splitview-nav-container --> </xul:box> <!-- .splitview-controller --> <xul:box class="splitview-side-details"/> <div id="splitview-templates" hidden="true"> <li id="splitview-tpl-summary-stylesheet" tabindex="0"> <a class="stylesheet-enabled" tabindex="0" href="#" title="&visibilityToggle.tooltip;" accesskey="&saveButton.accesskey;"></a> <hgroup class="stylesheet-info"> - <h1><a class="stylesheet-name" href="#"></a></h1> + <h1><a class="stylesheet-name" href="#"><xul:label crop="start"/></a></h1> <div class="stylesheet-more"> <h3 class="stylesheet-title"></h3> <h3 class="stylesheet-rule-count"></h3> <h3 class="stylesheet-error-message"></h3> <xul:spacer/> <h3><a class="stylesheet-saveButton" href="#" title="&saveButton.tooltip;" accesskey="&saveButton.accesskey;">&saveButton.label;</a></h3> </div> </hgroup> </li> <xul:box id="splitview-tpl-details-stylesheet" class="splitview-details"> <xul:resizer class="splitview-portrait-resizer" - dir="bottom" - element="stylesheets-controller"/> + dir="bottom" + element="splitview-resizer-target"/> <xul:toolbar id="splitview-details-toolbar" class="devtools-toolbar"> <xul:resizer class="splitview-landscape-resizer" dir="bottomend" - element="stylesheets-controller"/> + element="splitview-resizer-target"/> </xul:toolbar> <xul:box class="stylesheet-editor-input textbox" data-placeholder="&editorTextbox.placeholder;"/> </xul:box> </div> <!-- #splitview-templates --> </xul:box> <!-- .splitview-root --> <xul:script type="application/javascript"><![CDATA[
--- a/browser/devtools/styleeditor/test/Makefile.in +++ b/browser/devtools/styleeditor/test/Makefile.in @@ -48,17 +48,16 @@ include $(topsrcdir)/config/rules.mk browser_styleeditor_enabled.js \ browser_styleeditor_import.js \ browser_styleeditor_init.js \ browser_styleeditor_loading.js \ browser_styleeditor_new.js \ browser_styleeditor_pretty.js \ browser_styleeditor_readonly.js \ browser_styleeditor_reopen.js \ - browser_styleeditor_sv_filter.js \ browser_styleeditor_sv_keynav.js \ browser_styleeditor_sv_resize.js \ four.html \ head.js \ media.html \ media-small.css \ minified.html \ simple.css \
--- a/browser/devtools/styleeditor/test/browser_styleeditor_import.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_import.js @@ -1,16 +1,19 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // http rather than chrome to improve coverage const TESTCASE_URI = TEST_BASE_HTTP + "simple.html"; -Components.utils.import("resource://gre/modules/FileUtils.jsm"); +let tempScope = {}; +Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope); +let FileUtils = tempScope.FileUtils; + const FILENAME = "styleeditor-import-test.css"; const SOURCE = "body{background:red;}"; function test() { waitForExplicitFinish();
--- a/browser/devtools/styleeditor/test/browser_styleeditor_init.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js @@ -80,17 +80,17 @@ function testFirstStyleSheetEditor(aChro ok(!aEditor.hasFlag("inline"), "first stylesheet does not have INLINE flag"); let summary = aChrome.getSummaryElementForEditor(aEditor); ok(!summary.classList.contains("inline"), "first stylesheet UI does not have INLINE class"); - let name = summary.querySelector(".stylesheet-name").textContent; + let name = summary.querySelector(".stylesheet-name > label").getAttribute("value"); is(name, "simple.css", "first stylesheet's name is `simple.css`"); let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; is(parseInt(ruleCount), 1, "first stylesheet UI shows rule count as 1"); ok(summary.classList.contains("splitview-active"), @@ -108,17 +108,17 @@ function testSecondStyleSheetEditor(aChr ok(aEditor.hasFlag("inline"), "second stylesheet has INLINE flag"); let summary = aChrome.getSummaryElementForEditor(aEditor); ok(summary.classList.contains("inline"), "second stylesheet UI has INLINE class"); - let name = summary.querySelector(".stylesheet-name").textContent; + let name = summary.querySelector(".stylesheet-name > label").getAttribute("value"); ok(/^<.*>$/.test(name), "second stylesheet's name is surrounded by `<>`"); let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; is(parseInt(ruleCount), 3, "second stylesheet UI shows rule count as 3"); ok(!summary.classList.contains("splitview-active"),
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js @@ -1,16 +1,16 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ const TESTCASE_URI = TEST_BASE + "simple.html"; const TRANSITION_CLASS = "moz-styleeditor-transitioning"; - +const TESTCASE_CSS_SOURCE = "body{background-color:red;"; function test() { waitForExplicitFinish(); addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { aChrome.addChromeListener({ onContentAttach: run, @@ -74,19 +74,23 @@ function testEditorAdded(aChrome, aEdito let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; is(parseInt(ruleCount), 0, "new editor initially shows 0 rules"); let computedStyle = content.getComputedStyle(content.document.body, null); is(computedStyle.backgroundColor, "rgb(255, 255, 255)", "content's background color is initially white"); - for each (let c in "body{background-color:red;}") { + for each (let c in TESTCASE_CSS_SOURCE) { EventUtils.synthesizeKey(c, {}, gChromeWindow); } + + is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}", + "rule bracket has been auto-closed"); + }, gChromeWindow) ; }, onUpdate: function (aEditor) { gUpdateCount++; ok(content.document.documentElement.classList.contains(TRANSITION_CLASS), "StyleEditor's transition class has been added to content");
--- a/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js @@ -2,17 +2,20 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // http rather than chrome to improve coverage const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html"; const Cc = Components.classes; const Ci = Components.interfaces; -Components.utils.import("resource://gre/modules/FileUtils.jsm"); + +let tempScope = {}; +Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope); +let FileUtils = tempScope.FileUtils; function test() { waitForExplicitFinish(); addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { aChrome.addChromeListener({
deleted file mode 100644 --- a/browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js +++ /dev/null @@ -1,101 +0,0 @@ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -const TESTCASE_URI = TEST_BASE + "simple.html"; - - -function test() -{ - waitForExplicitFinish(); - - addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { - aChrome.addChromeListener({ - onContentAttach: run - }); - if (aChrome.isContentAttached) { - run(aChrome); - } - }); - - content.location = TESTCASE_URI; -} - -function getFilteredItemsCount(nav) -{ - let matches = nav.querySelectorAll("*.splitview-filtered"); - return matches ? matches.length : 0; -} - -function run(aChrome) -{ - aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach}); - aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach}); -} - -function onFirstEditorAttach(aEditor) -{ - let filter = gChromeWindow.document.querySelector(".splitview-filter"); - // force the command event on input since it is not possible to disable - // the search textbox's timeout. - let forceCommandEvent = function forceCommandEvent() { - let evt = gChromeWindow.document.createEvent("XULCommandEvent"); - evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false, - false, false, null); - filter.dispatchEvent(evt); - } - filter.addEventListener("input", forceCommandEvent, false); - - let nav = gChromeWindow.document.querySelector(".splitview-nav"); - nav.focus(); - - is(getFilteredItemsCount(nav), 0, - "there is 0 filtered item initially"); - - waitForFocus(function () { - // Search [s] (type-on-search since we focused nav above - not filter directly) - EventUtils.synthesizeKey("s", {}, gChromeWindow); - - // the search space is "simple.css" and "inline stylesheet #1" (2 sheets) - is(getFilteredItemsCount(nav), 0, - "there is 0 filtered item if searching for 's'"); - - EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si] - - is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered - "there is 1 filtered item if searching for 's'"); - - // use uppercase to check that filtering is case-insensitive - EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX] - is(getFilteredItemsCount(nav), 2, - "there is 2 filtered items if searching for 's'"); // no match - - // clear the search - EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow); - - is(filter.value, "", - "filter is back to empty"); - is(getFilteredItemsCount(nav), 0, - "there is 0 filtered item when filter is empty again"); - - for each (let c in "inline") { - EventUtils.synthesizeKey(c, {}, gChromeWindow); - } - - is(getFilteredItemsCount(nav), 1, // simple.css is filtered - "there is 1 filtered item if searching for 'inline'"); - - // auto-select the only result (enter the editor) - EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow); - - filter.removeEventListener("input", forceCommandEvent, false); - }, gChromeWindow); -} - -function onSecondEditorAttach(aEditor) -{ - ok(aEditor.sourceEditor.hasFocus(), - "second editor has been selected and focused automatically."); - - finish(); -}
--- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -1305,16 +1305,17 @@ InplaceEditor.prototype = { prevent = true; moveFocus(this.input.ownerDocument.defaultView, FOCUS_FORWARD); } else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE) { // Cancel and blur ourselves. |_onBlur| will call the user's // done handler for us. prevent = true; this.cancelled = true; this.input.blur(); + aEvent.stopPropagation(); } else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) { // No need for leading spaces here. This is particularly // noticable when adding a property: it's very natural to type // <name>: (which advances to the next property) then spacebar. prevent = !this.input.value; } if (prevent) {
--- a/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js +++ b/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js @@ -9,17 +9,16 @@ let stylePanel; function createDocument() { doc.body.innerHTML = '<style type="text/css"> ' + '.matches {color: #F00;}</style>' + '<span class="matches">Some styled text</span>' + '</div>'; doc.title = "Style Inspector key binding test"; - ok(window.StyleInspector, "StyleInspector exists"); stylePanel = new StyleInspector(window); Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); stylePanel.createPanel(false, function() { stylePanel.open(doc.body); }); } function runStyleInspectorTests()
--- a/browser/devtools/styleinspector/test/browser_bug683672.js +++ b/browser/devtools/styleinspector/test/browser_bug683672.js @@ -4,30 +4,32 @@ // Tests that the style inspector works properly let doc; let stylePanel; const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/test/browser_bug683672.html"; -Cu.import("resource:///modules/devtools/CssHtmlTree.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/devtools/CssHtmlTree.jsm", tempScope); +let CssHtmlTree = tempScope.CssHtmlTree; +let PropertyView = tempScope.PropertyView; function test() { waitForExplicitFinish(); addTab(TEST_URI); browser.addEventListener("load", tabLoaded, true); } function tabLoaded() { browser.removeEventListener("load", tabLoaded, true); doc = content.document; - ok(window.StyleInspector, "StyleInspector exists"); // ok(StyleInspector.isEnabled, "style inspector preference is enabled"); stylePanel = new StyleInspector(window); Services.obs.addObserver(runTests, "StyleInspector-opened", false); stylePanel.createPanel(false, function() { stylePanel.open(doc.body); }); }
--- a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js +++ b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js @@ -7,17 +7,16 @@ let doc; let stylePanel; function createDocument() { doc.body.innerHTML = "<div style='color:blue;'></div>"; doc.title = "Style Inspector Selector Text Test"; - ok(window.StyleInspector, "StyleInspector exists"); stylePanel = new StyleInspector(window); stylePanel.createPanel(false, function() { Services.obs.addObserver(SI_checkText, "StyleInspector-populated", false); let span = doc.querySelector("div"); ok(span, "captain, we have the test div");
--- a/browser/devtools/styleinspector/test/browser_csslogic_inherited.js +++ b/browser/devtools/styleinspector/test/browser_csslogic_inherited.js @@ -1,15 +1,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Test that inherited properties are treated correctly. -Cu.import("resource:///modules/devtools/CssLogic.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope); +let CssLogic = tempScope.CssLogic; let doc; function createDocument() { doc.body.innerHTML = '<div style="margin-left:10px; font-size: 5px"><div id="innerdiv">Inner div</div></div>'; doc.title = "Style Inspector Inheritance Test";
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_editor.js @@ -1,13 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource:///modules/devtools/CssRuleView.jsm"); +let tempScope = {} +Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope); +let CssRuleView = tempScope.CssRuleView; +let _ElementStyle = tempScope._ElementStyle; +let _editableField = tempScope._editableField; let doc = content.document; function expectDone(aValue, aCommit, aNext) { return function(aDoneValue, aDoneCommit) { dump("aDoneValue: " + aDoneValue + " commit: " + aDoneCommit + "\n"); @@ -113,9 +117,9 @@ function test() gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function(evt) { gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true); doc = content.document; waitForFocus(testReturnCommit, content); }, true); content.location = "data:text/html,inline editor tests"; -} \ No newline at end of file +}
--- a/browser/devtools/styleinspector/test/browser_ruleview_inherit.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_inherit.js @@ -1,13 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource:///modules/devtools/CssRuleView.jsm"); +let tempScope = {} +Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope); +let CssRuleView = tempScope.CssRuleView; +let _ElementStyle = tempScope._ElementStyle; +let _editableField = tempScope._editableField; let doc; function simpleInherit() { let style = '' + '#test2 {' + ' background-color: green;' +
--- a/browser/devtools/styleinspector/test/browser_ruleview_manipulation.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_manipulation.js @@ -1,13 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource:///modules/devtools/CssRuleView.jsm"); +let tempScope = {} +Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope); +let CssRuleView = tempScope.CssRuleView; +let _ElementStyle = tempScope._ElementStyle; +let _editableField = tempScope._editableField; let doc; function simpleOverride() { doc.body.innerHTML = '<div id="testid">Styled Node</div>'; let element = doc.getElementById("testid"); let elementStyle = new _ElementStyle(element);
--- a/browser/devtools/styleinspector/test/browser_ruleview_override.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_override.js @@ -1,13 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource:///modules/devtools/CssRuleView.jsm"); +let tempScope = {} +Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope); +let CssRuleView = tempScope.CssRuleView; +let _ElementStyle = tempScope._ElementStyle; +let _editableField = tempScope._editableField; let doc; function simpleOverride() { let style = '' + '#testid {' + ' background-color: blue;' +
--- a/browser/devtools/styleinspector/test/browser_ruleview_ui.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_ui.js @@ -1,13 +1,17 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource:///modules/devtools/CssRuleView.jsm"); +let tempScope = {} +Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope); +let CssRuleView = tempScope.CssRuleView; +let _ElementStyle = tempScope._ElementStyle; +let _editableField = tempScope._editableField; let doc; let ruleDialog; let ruleView; function waitForEditorFocus(aParent, aCallback) { aParent.addEventListener("focus", function onFocus(evt) {
--- a/browser/devtools/styleinspector/test/browser_styleinspector.js +++ b/browser/devtools/styleinspector/test/browser_styleinspector.js @@ -21,17 +21,16 @@ function createDocument() 'style list-items in the box at right. If you are reading this, ' + 'you should go do something else instead. Maybe read a book. Or better ' + 'yet, write some test-cases for another bit of code. ' + '<span style="font-style: italic">Maybe more inspector test-cases!</span></p>\n' + '<p id="closing">end transmission</p>\n' + '<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' + '</div>'; doc.title = "Style Inspector Test"; - ok(window.StyleInspector, "StyleInspector exists"); stylePanel = new StyleInspector(window); Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); stylePanel.createPanel(false, function() { stylePanel.open(doc.body); }); } function runStyleInspectorTests()
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_672744_search_filter.js +++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_672744_search_filter.js @@ -9,17 +9,16 @@ let stylePanel; function createDocument() { doc.body.innerHTML = '<style type="text/css"> ' + '.matches {color: #F00;}</style>' + '<span id="matches" class="matches">Some styled text</span>' + '</div>'; doc.title = "Style Inspector Search Filter Test"; - ok(window.StyleInspector, "StyleInspector exists"); // ok(StyleInspector.isEnabled, "style inspector preference is enabled"); stylePanel = new StyleInspector(window); Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); stylePanel.createPanel(false, function() { stylePanel.open(doc.body); }); }
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_672746_default_styles.js +++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_672746_default_styles.js @@ -9,17 +9,16 @@ let stylePanel; function createDocument() { doc.body.innerHTML = '<style type="text/css"> ' + '.matches {color: #F00;}</style>' + '<span id="matches" class="matches">Some styled text</span>' + '</div>'; doc.title = "Style Inspector Default Styles Test"; - ok(window.StyleInspector, "StyleInspector exists"); // ok(StyleInspector.isEnabled, "style inspector preference is enabled"); stylePanel = new StyleInspector(window); Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); stylePanel.createPanel(false, function() { stylePanel.open(doc.body); }); }
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_689759_no_results_placeholder.js +++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_689759_no_results_placeholder.js @@ -8,17 +8,16 @@ let doc; let stylePanel; function createDocument() { doc.body.innerHTML = '<style type="text/css"> ' + '.matches {color: #F00;}</style>' + '<span id="matches" class="matches">Some styled text</span>'; doc.title = "Tests that the no results placeholder works properly"; - ok(window.StyleInspector, "StyleInspector exists"); stylePanel = new StyleInspector(window); Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); stylePanel.createPanel(false, function() { stylePanel.open(doc.body); }); } function runStyleInspectorTests()
--- a/browser/devtools/styleinspector/test/head.js +++ b/browser/devtools/styleinspector/test/head.js @@ -30,18 +30,22 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -Cu.import("resource:///modules/devtools/StyleInspector.jsm"); -Cu.import("resource://gre/modules/HUDService.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/devtools/StyleInspector.jsm", tempScope); +Cu.import("resource://gre/modules/HUDService.jsm", tempScope); +let StyleInspector = tempScope.StyleInspector; +let HUDService = tempScope.HUDService; +let ConsoleUtils = tempScope.ConsoleUtils; function log(aMsg) { dump("*** WebConsoleTest: " + aMsg + "\n"); } function pprint(aObj) {
--- a/browser/devtools/tilt/test/head.js +++ b/browser/devtools/tilt/test/head.js @@ -1,19 +1,29 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /*global Services, Components, gBrowser, executeSoon, info */ /*global InspectorUI, Tilt, TiltGL, EPSILON */ "use strict"; -Components.utils.import("resource:///modules/devtools/TiltGL.jsm"); -Components.utils.import("resource:///modules/devtools/TiltMath.jsm"); -Components.utils.import("resource:///modules/devtools/TiltUtils.jsm"); -Components.utils.import("resource:///modules/devtools/TiltVisualizer.jsm"); +let tempScope = {}; +Components.utils.import("resource:///modules/devtools/TiltGL.jsm", tempScope); +Components.utils.import("resource:///modules/devtools/TiltMath.jsm", tempScope); +Components.utils.import("resource:///modules/devtools/TiltUtils.jsm", tempScope); +Components.utils.import("resource:///modules/devtools/TiltVisualizer.jsm", tempScope); +let TiltGL = tempScope.TiltGL; +let EPSILON = tempScope.EPSILON; +let TiltMath = tempScope.TiltMath; +let vec3 = tempScope.vec3; +let mat3 = tempScope.mat3; +let mat4 = tempScope.mat4; +let quat4 = tempScope.quat4; +let TiltUtils = tempScope.TiltUtils; +let TiltVisualizer = tempScope.TiltVisualizer; const DEFAULT_HTML = "data:text/html," + "<DOCTYPE html>" + "<html>" + "<head>" + "<title>Three Laws</title>" + "</head>" +
--- a/browser/devtools/webconsole/test/browser_gcli_inspect.js +++ b/browser/devtools/webconsole/test/browser_gcli_inspect.js @@ -2,17 +2,19 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ // For more information on GCLI see: // - https://github.com/mozilla/gcli/blob/master/docs/index.md // - https://wiki.mozilla.org/DevTools/Features/GCLI // Tests that the inspect command works as it should -Components.utils.import("resource:///modules/gcli.jsm"); +let tempScope = {}; +Components.utils.import("resource:///modules/gcli.jsm", tempScope); +let gcli = tempScope.gcli; registerCleanupFunction(function() { gcliterm = undefined; requisition = undefined; Services.prefs.clearUserPref("devtools.gcli.enable"); });
--- a/browser/devtools/webconsole/test/browser_gcli_integrate.js +++ b/browser/devtools/webconsole/test/browser_gcli_integrate.js @@ -3,17 +3,19 @@ // For more information on GCLI see: // - https://github.com/mozilla/gcli/blob/master/docs/index.md // - https://wiki.mozilla.org/DevTools/Features/GCLI // Tests that source URLs in the Web Console can be clicked to display the // standard View Source window. -Components.utils.import("resource:///modules/gcli.jsm"); +let tempScope = {}; +Components.utils.import("resource:///modules/gcli.jsm", tempScope); +let gcli = tempScope.gcli; let require = gcli._internal.require; const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; registerCleanupFunction(function() { require = undefined; Services.prefs.clearUserPref("devtools.gcli.enable"); });
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js @@ -5,18 +5,16 @@ * * Contributor(s): * Patrick Walton <pcwalton@mozilla.com> * * ***** END LICENSE BLOCK ***** */ // Tests that appropriately-localized timestamps are printed. -Cu.import("resource:///modules/HUDService.jsm"); - const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testTimestamp, false); function testTimestamp() {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js @@ -32,16 +32,17 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const TEST_URI = "data:text/html,<p>bug 585991 - autocomplete popup keyboard usage test"; +let HUD; registerCleanupFunction(function() { Services.prefs.clearUserPref("devtools.gcli.enable"); }); function test() { Services.prefs.setBoolPref("devtools.gcli.enable", false); addTab(TEST_URI);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_586388_select_all.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_586388_select_all.js @@ -67,13 +67,11 @@ function testSelectionWhenMovingBetweenB selectAllItem.dispatchEvent(commandEvent); is(outputNode.selectedCount, outputNode.childNodes.length, "all console " + "messages are selected after performing a select-all operation from " + "the context menu"); outputNode.selectedIndex = -1; - commandEvent = contextMenu = groupNode = range = null; - finishTest(); }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js @@ -12,17 +12,17 @@ let inputNode, values; function tabLoad(aEvent) { browser.removeEventListener(aEvent.type, arguments.callee, true); waitForFocus(function() { openConsole(); let hudId = HUDService.getHudIdByWindow(content); - HUD = HUDService.hudReferences[hudId]; + let HUD = HUDService.hudReferences[hudId]; inputNode = HUD.jsterm.inputNode; inputNode.focus(); ok(!inputNode.value, "inputNode.value is empty"); values = ["document", "window", "document.body"];
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js @@ -86,16 +86,17 @@ let inputValues = [ // 17 [true, "({a:'b', c:'d', e:1, f:'2'})", '({a:"b", c:"d", e:1, f:"2"})', "[object Object", '({a:"b", c:"d", e:1, f:"2"})'], ]; let eventHandlers = []; let popupShown = []; +let HUD; function tabLoad(aEvent) { browser.removeEventListener(aEvent.type, arguments.callee, true); waitForFocus(function () { openConsole(); let hudId = HUDService.getHudIdByWindow(content); @@ -216,17 +217,16 @@ function testEnd() { } for (let i = 0; i < inputValues.length; i++) { if (inputValues[i][0] && !popupShown[i]) { ok(false, "the property panel failed to show for inputValues[" + i + "]"); } } - eventHandlers = popupshown = null; executeSoon(finishTest); } registerCleanupFunction(function() { Services.prefs.clearUserPref("devtools.gcli.enable"); }); function test() {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_642108_pruneTest.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_642108_pruneTest.js @@ -16,31 +16,31 @@ const SEVERITY_WARNING = 1; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testCSSPruning, false); } function populateConsoleRepeats(aHudRef) { let hud = aHudRef.HUDBox; - for (i = 0; i < 5; i++) { + for (let i = 0; i < 5; i++) { let node = ConsoleUtils.createMessageNode(hud.ownerDocument, CATEGORY_CSS, SEVERITY_WARNING, "css log x", aHudRef.hudId); ConsoleUtils.outputMessageNode(node, aHudRef.hudId); } } function populateConsole(aHudRef) { let hud = aHudRef.HUDBox; - for (i = 0; i < LOG_LIMIT + 5; i++) { + for (let i = 0; i < LOG_LIMIT + 5; i++) { let node = ConsoleUtils.createMessageNode(hud.ownerDocument, CATEGORY_CSS, SEVERITY_WARNING, "css log " + i, aHudRef.hudId); ConsoleUtils.outputMessageNode(node, aHudRef.hudId); } }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js @@ -1,17 +1,22 @@ /* vim:set ts=2 sw=2 sts=2 et: */ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that document.body autocompletes in the web console. -Cu.import("resource:///modules/PropertyPanel.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/PropertyPanel.jsm", tempScope); +let PropertyPanel = tempScope.PropertyPanel; +let PropertyTreeView = tempScope.PropertyTreeView; +let namesAndValuesOf = tempScope.namesAndValuesOf; +let isNonNativeGetter = tempScope.isNonNativeGetter; registerCleanupFunction(function() { Services.prefs.clearUserPref("devtools.gcli.enable"); }); function test() { Services.prefs.setBoolPref("devtools.gcli.enable", false); addTab("data:text/html,Web Console autocompletion bug in document.body");
--- a/browser/devtools/webconsole/test/head.js +++ b/browser/devtools/webconsole/test/head.js @@ -31,17 +31,20 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -Cu.import("resource:///modules/HUDService.jsm"); +let tempScope = {}; +Cu.import("resource:///modules/HUDService.jsm", tempScope); +let HUDService = tempScope.HUDService; +let ConsoleUtils = tempScope.ConsoleUtils; function log(aMsg) { dump("*** WebConsoleTest: " + aMsg + "\n"); } function pprint(aObj) {
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -364,20 +364,18 @@ @BINPATH@/components/nsFormAutoComplete.js @BINPATH@/components/nsFormHistory.js @BINPATH@/components/nsInputListAutoComplete.js @BINPATH@/components/contentSecurityPolicy.manifest @BINPATH@/components/contentSecurityPolicy.js @BINPATH@/components/contentAreaDropListener.manifest @BINPATH@/components/contentAreaDropListener.js #ifdef MOZ_B2G_RIL -@BINPATH@/components/nsTelephonyWorker.manifest -@BINPATH@/components/nsTelephonyWorker.js -@BINPATH@/components/Telephony.manifest -@BINPATH@/components/Telephony.js +@BINPATH@/components/RadioInterfaceLayer.manifest +@BINPATH@/components/RadioInterfaceLayer.js @BINPATH@/components/nsWifiWorker.js @BINPATH@/components/nsWifiWorker.manifest #endif @BINPATH@/components/BrowserProfileMigrators.manifest @BINPATH@/components/ChromeProfileMigrator.js @BINPATH@/components/FirefoxProfileMigrator.js #ifdef XP_MACOSX @BINPATH@/components/libalerts.dylib
--- a/browser/locales/en-US/chrome/browser/devtools/styleeditor.dtd +++ b/browser/locales/en-US/chrome/browser/devtools/styleeditor.dtd @@ -17,27 +17,16 @@ <!ENTITY newButton.accesskey "N"> <!ENTITY newButton.commandkey "n"> <!ENTITY importButton.label "Import…"> <!ENTITY importButton.tooltip "Import and append an existing style sheet to the document"> <!ENTITY importButton.accesskey "I"> <!ENTITY importButton.commandkey "i"> -<!ENTITY searchInput.tooltip "Filter style sheets by name"> -<!ENTITY searchInput.placeholder "Find style sheet"> - -<!-- LOCALIZATION NOTE (searchNoResults): This is shown when searching a term - that is not found in any stylesheet or stylesheet name. --> -<!ENTITY searchNoResults.label "No matching style sheet has been found."> - -<!-- LOCALIZATION NOTE (searchClearButton): This button clears the search input - box and is visible only when a search term has been typed. --> -<!ENTITY searchClearButton.label "Clear"> - <!ENTITY visibilityToggle.tooltip "Toggle style sheet visibility"> <!ENTITY visibilityToggle.accesskey "V"> <!ENTITY saveButton.label "Save"> <!ENTITY saveButton.tooltip "Save this style sheet to a file"> <!ENTITY saveButton.accesskey "S"> <!ENTITY saveButton.commandkey "s"> @@ -55,8 +44,11 @@ tip sentence shown when there is no stylesheet. It suggests to create a new stylesheet and provides an action link to do so. --> <!ENTITY noStyleSheet-tip-start.label "Perhaps you'd like to "> <!-- LOCALICATION NOTE (noStyleSheet-tip-action.label): This is text for the link that triggers creation of a new stylesheet. --> <!ENTITY noStyleSheet-tip-action.label "append a new style sheet"> <!-- LOCALICATION NOTE (noStyleSheet-tip-end.label): End of the tip sentence --> <!ENTITY noStyleSheet-tip-end.label "?"> + +<!-- LOCALIZATION NOTE (closeCmd.key): Accel + this key closes the window. --> +<!ENTITY closeCmd.key "W">
--- a/browser/modules/Makefile.in +++ b/browser/modules/Makefile.in @@ -47,16 +47,17 @@ include $(topsrcdir)/config/config.mk ifdef ENABLE_TESTS DIRS += test endif EXTRA_JS_MODULES = \ openLocationLastURL.jsm \ NetworkPrioritizer.jsm \ offlineAppCache.jsm \ + TelemetryTimestamps.jsm \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),windows) EXTRA_JS_MODULES += \ WindowsPreviewPerTab.jsm \ WindowsJumpLists.jsm \ $(NULL) endif
new file mode 100644 --- /dev/null +++ b/browser/modules/TelemetryTimestamps.jsm @@ -0,0 +1,26 @@ +/* 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/. */ + +let EXPORTED_SYMBOLS = ["TelemetryTimestamps"]; + +let TelemetryTimestamps = { + timeStamps: {}, + add: function TT_add(name, value) { + // Default to "now" if not specified + if (value == null) + value = Date.now(); + + if (isNaN(value)) + throw new Error("Value must be a timestamp"); + + // If there's an existing value, just ignore the new value. + if (this.timeStamps.hasOwnProperty(name)) + return; + + this.timeStamps[name] = value; + }, + get: function TT_get() { + return JSON.parse(JSON.stringify(this.timeStamps)); + } +};
--- a/browser/modules/test/Makefile.in +++ b/browser/modules/test/Makefile.in @@ -40,16 +40,17 @@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = browser/modules/test include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _BROWSER_FILES = \ browser_NetworkPrioritizer.js \ + browser_TelemetryTimestamps.js \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),windows) _BROWSER_FILES += \ browser_taskbar_preview.js \ $(NULL) endif
new file mode 100644 --- /dev/null +++ b/browser/modules/test/browser_TelemetryTimestamps.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function getSimpleMeasurementsFromTelemetryPing() { + const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver); + let str = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString); + TelemetryPing.observe(str, "get-payload", ""); + + return JSON.parse(str.data).simpleMeasurements; +} + +function test() { + // Test the module logic + Cu.import("resource:///modules/TelemetryTimestamps.jsm"); + let now = Date.now(); + TelemetryTimestamps.add("foo"); + let fooValue = TelemetryTimestamps.get().foo; + ok(fooValue, "foo was added"); + ok(fooValue >= now, "foo has a reasonable value"); + + // Add timestamp with value + TelemetryTimestamps.add("bar", 1); + ok(TelemetryTimestamps.get().bar, "bar was added"); + is(TelemetryTimestamps.get().bar, 1, "bar has the right value"); + + // Can't add the same timestamp twice + TelemetryTimestamps.add("bar", 2); + is(TelemetryTimestamps.get().bar, 1, "bar wasn't overwritten"); + + let threw = false; + try { + TelemetryTimestamps.add("baz", "this isn't a number"); + } catch (ex) { + threw = true; + } + ok(threw, "adding baz threw"); + ok(!TelemetryTimestamps.get().baz, "no baz was added"); + + // Test that the data gets added to the telemetry ping properly + let simpleMeasurements = getSimpleMeasurementsFromTelemetryPing(); + ok(simpleMeasurements, "got simple measurements from ping data"); + is(simpleMeasurements.foo, fooValue, "foo was included"); + is(simpleMeasurements.bar, 1, "bar was included"); + ok(!simpleMeasurements.baz, "baz wasn't included since it wasn't added"); + + // Check browser timestamps that we add + let props = [ + // These can't be reliably tested when the test is run alone + //"delayedStartupStarted", + //"delayedStartupFinished", + "sessionRestoreInitialized", + // This doesn't get hit in the testing profile + //"sessionRestoreRestoring" + ]; + + props.forEach(function (p) { + let value = simpleMeasurements[p]; + ok(value, p + " exists"); + ok(!isNaN(value), p + " is a number"); + ok(value > 0 && value < Date.now(), p + " value is reasonable"); + }); +}
deleted file mode 100644 index 2527df6e72b5c80b91e180af9644ba96a5c148d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 389f689aa2a7611f53977594c048f79c4991affd..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 2527df6e72b5c80b91e180af9644ba96a5c148d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 75d6f13d8c6e6a1cfc0d5766cfec91378e0b60c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 2527df6e72b5c80b91e180af9644ba96a5c148d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 33bb4a320e76cc7a346ca4beb60e89a85a48aa2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
--- a/build/autoconf/mozconfig2client-mk +++ b/build/autoconf/mozconfig2client-mk @@ -44,18 +44,16 @@ # See mozconfig2configure for more details print_header() { _mozconfig=${MOZCONFIG:-$HOME/.mozconfig} cat >> $tmp_file <<EOF # gmake # This file is automatically generated for client.mk. # Do not edit. Edit $_mozconfig instead. -# To create a new .mozconfig file, you can visit, -# http://webtools.mozilla.org/build/config.cgi EOF } ac_add_options() { echo "# $* is used by configure (not client.mk)" >> $tmp_file }
--- a/build/mobile/devicemanager.py +++ b/build/mobile/devicemanager.py @@ -57,252 +57,292 @@ class DMError(Exception): def __init__(self, msg= ''): self.msg = msg def __str__(self): return self.msg +def abstractmethod(method): + line = method.func_code.co_firstlineno + filename = method.func_code.co_filename + def not_implemented(*args, **kwargs): + raise NotImplementedError('Abstract method %s at File "%s", line %s \ + should be implemented by a concrete class' % + (repr(method), filename,line)) + return not_implemented + class DeviceManager: - # external function - # returns: - # success: True - # failure: False + + @abstractmethod def pushFile(self, localname, destname): - assert 0 == 1 - return False - - # external function - # returns: - # success: directory name - # failure: None + """ + external function + returns: + success: True + failure: False + """ + + @abstractmethod def mkDir(self, name): - assert 0 == 1 - return None - - # make directory structure on the device - # external function - # returns: - # success: directory structure that we created - # failure: None + """ + external function + returns: + success: directory name + failure: None + """ + + @abstractmethod def mkDirs(self, filename): - assert 0 == 1 - return None - - # push localDir from host to remoteDir on the device - # external function - # returns: - # success: remoteDir - # failure: None + """ + make directory structure on the device + external function + returns: + success: directory structure that we created + failure: None + """ + + @abstractmethod def pushDir(self, localDir, remoteDir): - assert 0 == 1 - return None - - # external function - # returns: - # success: True - # failure: False - def dirExists(self, dirname): - assert 0 == 1 - return False - - # Because we always have / style paths we make this a lot easier with some - # assumptions - # external function - # returns: - # success: True - # failure: False - def fileExists(self, filepath): - assert 0 == 1 - return False + """ + push localDir from host to remoteDir on the device + external function + returns: + success: remoteDir + failure: None + """ - # list files on the device, requires cd to directory first - # external function - # returns: - # success: array of filenames, ['file1', 'file2', ...] - # failure: [] + @abstractmethod + def dirExists(self, dirname): + """ + external function + returns: + success: True + failure: False + """ + + @abstractmethod + def fileExists(self, filepath): + """ + Because we always have / style paths we make this a lot easier with some + assumptions + external function + returns: + success: True + failure: False + """ + + @abstractmethod def listFiles(self, rootdir): - assert 0 == 1 - return [] - - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None + """ + list files on the device, requires cd to directory first + external function + returns: + success: array of filenames, ['file1', 'file2', ...] + failure: None + """ + + @abstractmethod def removeFile(self, filename): - assert 0 == 1 - return False - - # does a recursive delete of directory on the device: rm -Rf remoteDir - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None + """ + external function + returns: + success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" + failure: None + """ + + @abstractmethod def removeDir(self, remoteDir): - assert 0 == 1 - return None - - # external function - # returns: - # success: array of process tuples - # failure: [] + """ + does a recursive delete of directory on the device: rm -Rf remoteDir + external function + returns: + success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" + failure: None + """ + + @abstractmethod def getProcessList(self): - assert 0 == 1 - return [] - - # external function - # returns: - # success: pid - # failure: None + """ + external function + returns: + success: array of process tuples + failure: None + """ + + @abstractmethod def fireProcess(self, appname, failIfRunning=False): - assert 0 == 1 - return None - - # external function - # returns: - # success: output filename - # failure: None + """ + external function + returns: + success: pid + failure: None + """ + + @abstractmethod def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False): - assert 0 == 1 - return None - - # loops until 'process' has exited or 'timeout' seconds is reached - # loop sleeps for 'interval' seconds between iterations - # external function - # returns: - # success: [file contents, None] - # failure: [None, None] + """ + external function + returns: + success: output filename + failure: None + """ + def communicate(self, process, timeout = 600, interval = 5): + """ + loops until 'process' has exited or 'timeout' seconds is reached + loop sleeps for 'interval' seconds between iterations + external function + returns: + success: [file contents, None] + failure: [None, None] + """ + timed_out = True if (timeout > 0): total_time = 0 while total_time < timeout: time.sleep(interval) if self.processExist(process) == None: timed_out = False break total_time += interval if (timed_out == True): return [None, None] return [self.getFile(process, "temp.txt"), None] - # iterates process list and returns pid if exists, otherwise None - # external function - # returns: - # success: pid - # failure: None def processExist(self, appname): + """ + iterates process list and returns pid if exists, otherwise None + external function + returns: + success: pid + failure: None + """ + pid = None #filter out extra spaces parts = filter(lambda x: x != '', appname.split(' ')) appname = ' '.join(parts) #filter out the quoted env string if it exists #ex: '"name=value;name2=value2;etc=..." process args' -> 'process args' parts = appname.split('"') if (len(parts) > 2): appname = ' '.join(parts[2:]).strip() pieces = appname.split(' ') parts = pieces[0].split('/') app = parts[-1] + procre = re.compile('.*' + app + '.*') procList = self.getProcessList() if (procList == []): return None for proc in procList: - procName = proc[1].split('/')[-1] - if (procName == app): + if (procre.match(proc[1])): pid = proc[0] break return pid - # external function - # returns: - # success: output from testagent - # failure: None + + @abstractmethod def killProcess(self, appname): - assert 0 == 1 - return None - - # external function - # returns: - # success: filecontents - # failure: None + """ + external function + returns: + success: output from testagent + failure: None + """ + + @abstractmethod def catFile(self, remoteFile): - assert 0 == 1 - return None - - # external function - # returns: - # success: output of pullfile, string - # failure: None + """ + external function + returns: + success: filecontents + failure: None + """ + + @abstractmethod def pullFile(self, remoteFile): - assert 0 == 1 - return None - - # copy file from device (remoteFile) to host (localFile) - # external function - # returns: - # success: output of pullfile, string - # failure: None + """ + external function + returns: + success: output of pullfile, string + failure: None + """ + + @abstractmethod def getFile(self, remoteFile, localFile = ''): - assert 0 == 1 - return None - - # copy directory structure from device (remoteDir) to host (localDir) - # external function - # checkDir exists so that we don't create local directories if the - # remote directory doesn't exist but also so that we don't call isDir - # twice when recursing. - # returns: - # success: list of files, string - # failure: None + """ + copy file from device (remoteFile) to host (localFile) + external function + returns: + success: output of pullfile, string + failure: None + """ + + @abstractmethod def getDirectory(self, remoteDir, localDir, checkDir=True): - assert 0 == 1 - return None - - # external function - # returns: - # success: True - # failure: False - # Throws a FileError exception when null (invalid dir/filename) + """ + copy directory structure from device (remoteDir) to host (localDir) + external function + checkDir exists so that we don't create local directories if the + remote directory doesn't exist but also so that we don't call isDir + twice when recursing. + returns: + success: list of files, string + failure: None + """ + + @abstractmethod def isDir(self, remotePath): - assert 0 == 1 - return False - - # true/false check if the two files have the same md5 sum - # external function - # returns: - # success: True - # failure: False + """ + external function + returns: + success: True + failure: False + Throws a FileError exception when null (invalid dir/filename) + """ + + @abstractmethod def validateFile(self, remoteFile, localFile): - assert 0 == 1 - return False - - # return the md5 sum of a remote file - # internal function - # returns: - # success: MD5 hash for given filename - # failure: None + """ + true/false check if the two files have the same md5 sum + external function + returns: + success: True + failure: False + """ + + @abstractmethod def getRemoteHash(self, filename): - assert 0 == 1 - return None - - # return the md5 sum of a file on the host - # internal function - # returns: - # success: MD5 hash for given filename - # failure: None + """ + return the md5 sum of a remote file + internal function + returns: + success: MD5 hash for given filename + failure: None + """ + def getLocalHash(self, filename): + """ + return the md5 sum of a file on the host + internal function + returns: + success: MD5 hash for given filename + failure: None + """ + file = open(filename, 'rb') if (file == None): return None try: mdsum = hashlib.md5() except: return None @@ -312,162 +352,201 @@ class DeviceManager: if not data: break mdsum.update(data) file.close() hexval = mdsum.hexdigest() if (self.debug >= 3): print "local hash returned: '" + hexval + "'" return hexval - # Gets the device root for the testing area on the device - # For all devices we will use / type slashes and depend on the device-agent - # to sort those out. The agent will return us the device location where we - # should store things, we will then create our /tests structure relative to - # that returned path. - # Structure on the device is as follows: - # /tests - # /<fennec>|<firefox> --> approot - # /profile - # /xpcshell - # /reftest - # /mochitest - # - # external function - # returns: - # success: path for device root - # failure: None + + @abstractmethod def getDeviceRoot(self): - assert 0 == 1 + """ + Gets the device root for the testing area on the device + For all devices we will use / type slashes and depend on the device-agent + to sort those out. The agent will return us the device location where we + should store things, we will then create our /tests structure relative to + that returned path. + Structure on the device is as follows: + /tests + /<fennec>|<firefox> --> approot + /profile + /xpcshell + /reftest + /mochitest + external + returns: + success: path for device root + failure: None + """ + + def getAppRoot(self): + """ + Either we will have /tests/fennec or /tests/firefox but we will never have + both. Return the one that exists + TODO: ensure we can support org.mozilla.firefox + external function + returns: + success: path for app root + failure: None + """ + + devroot = self.getDeviceRoot() + if (devroot == None): + return None + + if (self.dirExists(devroot + '/fennec')): + return devroot + '/fennec' + elif (self.dirExists(devroot + '/firefox')): + return devroot + '/firefox' + elif (self.dirExsts('/data/data/org.mozilla.fennec')): + return 'org.mozilla.fennec' + elif (self.dirExists('/data/data/org.mozilla.firefox')): + return 'org.mozilla.firefox' + elif (self.dirExists('/data/data/org.mozilla.fennec_aurora')): + return 'org.mozilla.fennec_aurora' + elif (self.dirExists('/data/data/org.mozilla.firefox_beta')): + return 'org.mozilla.firefox_beta' + + # Failure (either not installed or not a recognized platform) return None - # Either we will have /tests/fennec or /tests/firefox but we will never have - # both. Return the one that exists - # TODO: ensure we can support org.mozilla.firefox - # external function - # returns: - # success: path for app root - # failure: None - def getAppRoot(self): - assert 0 == 1 - return None - - # Gets the directory location on the device for a specific test type - # Type is one of: xpcshell|reftest|mochitest - # external function - # returns: - # success: path for test root - # failure: None def getTestRoot(self, type): + """ + Gets the directory location on the device for a specific test type + Type is one of: xpcshell|reftest|mochitest + external function + returns: + success: path for test root + failure: None + """ + devroot = self.getDeviceRoot() if (devroot == None): return None if (re.search('xpcshell', type, re.I)): self.testRoot = devroot + '/xpcshell' elif (re.search('?(i)reftest', type)): self.testRoot = devroot + '/reftest' elif (re.search('?(i)mochitest', type)): self.testRoot = devroot + '/mochitest' return self.testRoot - # Sends a specific process ID a signal code and action. - # For Example: SIGINT and SIGDFL to process x def signal(self, processID, signalType, signalAction): - # currently not implemented in device agent - todo + """ + Sends a specific process ID a signal code and action. + For Example: SIGINT and SIGDFL to process x + """ + #currently not implemented in device agent - todo + pass - # Get a return code from process ending -- needs support on device-agent def getReturnCode(self, processID): + """Get a return code from process ending -- needs support on device-agent""" # TODO: make this real + return 0 - # external function - # returns: - # success: output of unzip command - # failure: None + @abstractmethod def unpackFile(self, filename): - return None - - # external function - # returns: - # success: status from test agent - # failure: None + """ + external function + returns: + success: output of unzip command + failure: None + """ + + @abstractmethod def reboot(self, ipAddr=None, port=30000): - assert 0 == 1 - return None - - # validate localDir from host to remoteDir on the device - # external function - # returns: - # success: True - # failure: False + """ + external function + returns: + success: status from test agent + failure: None + """ + def validateDir(self, localDir, remoteDir): + """ + validate localDir from host to remoteDir on the device + external function + returns: + success: True + failure: False + """ + if (self.debug >= 2): print "validating directory: " + localDir + " to " + remoteDir for root, dirs, files in os.walk(localDir): parts = root.split(localDir) for file in files: remoteRoot = remoteDir + '/' + parts[1] remoteRoot = remoteRoot.replace('/', '/') if (parts[1] == ""): remoteRoot = remoteDir remoteName = remoteRoot + '/' + file if (self.validateFile(remoteName, os.path.join(root, file)) <> True): return False return True - - # Returns information about the device: - # Directive indicates the information you want to get, your choices are: - # os - name of the os - # id - unique id of the device - # uptime - uptime of the device - # systime - system time of the device - # screen - screen resolution - # memory - memory stats - # process - list of running processes (same as ps) - # disk - total, free, available bytes on disk - # power - power status (charge, battery temp) - # all - all of them - or call it with no parameters to get all the information - # returns: - # success: dict of info strings by directive name - # failure: {} + + @abstractmethod def getInfo(self, directive=None): - assert 0 == 1 - return {} - - # external function - # returns: - # success: output from agent for inst command - # failure: None + """ + Returns information about the device: + Directive indicates the information you want to get, your choices are: + os - name of the os + id - unique id of the device + uptime - uptime of the device + systime - system time of the device + screen - screen resolution + memory - memory stats + process - list of running processes (same as ps) + disk - total, free, available bytes on disk + power - power status (charge, battery temp) + all - all of them - or call it with no parameters to get all the information + returns: + success: dict of info strings by directive name + failure: None + """ + + @abstractmethod def installApp(self, appBundlePath, destPath=None): - assert 0 == 1 - return None - - # external function - # returns: - # success: True - # failure: None + """ + external function + returns: + success: output from agent for inst command + failure: None + """ + + @abstractmethod def uninstallAppAndReboot(self, appName, installPath=None): - assert 0 == 1 - return None - - # external function - # returns: - # success: text status from command or callback server - # failure: None - def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000): - assert 0 == 1 - return None - - # external function - # returns: - # success: time in ms - # failure: None + """ + external function + returns: + success: True + failure: None + """ + + @abstractmethod + def updateApp(self, appBundlePath, processName=None, + destPath=None, ipAddr=None, port=30000): + """ + external function + returns: + success: text status from command or callback server + failure: None + """ + + @abstractmethod def getCurrentTime(self): - assert 0 == 1 - return None - + """ + external function + returns: + success: time in ms + failure: None + """ class NetworkTools: def __init__(self): pass # Utilities to get the local ip address def getInterfaceIp(self, ifname): if os.name != "nt": @@ -478,20 +557,17 @@ class NetworkTools: s.fileno(), 0x8915, # SIOCGIFADDR struct.pack('256s', ifname[:15]) )[20:24]) else: return None def getLanIp(self): - try: - ip = socket.gethostbyname(socket.gethostname()) - except socket.gaierror: - ip = socket.gethostbyname(socket.gethostname() + ".local") # for Mac OS X + ip = socket.gethostbyname(socket.gethostname()) if ip.startswith("127.") and os.name != "nt": interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"] for ifname in interfaces: try: ip = self.getInterfaceIp(ifname) break; except IOError: pass @@ -516,9 +592,8 @@ class NetworkTools: if seed > maxportnum: print "Could not find open port after checking 5000 ports" raise seed += 1 except: print "Socket error trying to find open port" return seed -
--- a/build/mobile/devicemanagerADB.py +++ b/build/mobile/devicemanagerADB.py @@ -110,24 +110,27 @@ class DeviceManagerADB(DeviceManager): # adb "push" accepts a directory as an argument, but if the directory # contains symbolic links, the links are pushed, rather than the linked # files; we either zip/unzip or push file-by-file to get around this # limitation try: if (not self.dirExists(remoteDir)): self.mkDirs(remoteDir+"/x") if (self.useZip): - localZip = tempfile.mktemp()+".zip" - remoteZip = remoteDir + "/adbdmtmp.zip" - subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir) - self.pushFile(localZip, remoteZip) - os.remove(localZip) - data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read() - self.checkCmdAs(["shell", "rm", remoteZip]) - if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)): + try: + localZip = tempfile.mktemp()+".zip" + remoteZip = remoteDir + "/adbdmtmp.zip" + subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir) + self.pushFile(localZip, remoteZip) + os.remove(localZip) + data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read() + self.checkCmdAs(["shell", "rm", remoteZip]) + if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)): + raise Exception("unzip failed, or permissions error") + except: print "zip/unzip failure: falling back to normal push" self.useZip = False self.pushDir(localDir, remoteDir) else: for root, dirs, files in os.walk(localDir, followlinks='true'): relRoot = os.path.relpath(root, localDir) for file in files: localFile = os.path.join(root, file)
--- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -40,17 +40,17 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ TESTPATH = $(topsrcdir)/mobile/android/base/tests include $(DEPTH)/config/autoconf.mk MODULE = robocop -ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.0.jar +ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.1.jar JAVAFILES = \ R.java \ _JAVA_HARNESS = \ Actions.java \ Assert.java \ Driver.java \
--- a/build/mobile/robocop/README +++ b/build/mobile/robocop/README @@ -1,9 +1,9 @@ Robocop is a Mozilla project which uses Robotium to test Firefox on Android devices. Robotium is an open source tool licensed under the Apache 2.0 license and the original source can be found here: http://code.google.com/p/robotium/ -We are including robotium-solo-3.0.jar as a binary and are not modifying it in anyway +We are including robotium-solo-3.1.jar as a binary and are not modifying it in anyway from the original download found at: http://code.google.com/p/robotium/
deleted file mode 100644 index 3305251f61d61702e0f3dd61f67eb11e984ac587..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
new file mode 100644 index 0000000000000000000000000000000000000000..0ba05859b90a8c29ca519e228674db9aebe69d90 GIT binary patch literal 49691 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 KV6(Nqwf+y-lXzPI
--- a/build/unix/build-toolchain/build-gcc.py +++ b/build/unix/build-toolchain/build-gcc.py @@ -66,16 +66,24 @@ def build_one_stage(env, stage_dir): "--with-mpfr=%s" % lib_inst_dir]) tool_inst_dir = stage_dir + '/inst' binutils_build_dir = stage_dir + '/binutils' build_package(binutils_source_dir, binutils_build_dir, ["--prefix=%s" % tool_inst_dir]) + glibc_build_dir = stage_dir + '/glibc' + build_package(glibc_source_dir, glibc_build_dir, + ["--disable-profile", + "--enable-add-ons=nptl", + "--without-selinux", + "--enable-kernel=2.6.25", + "--prefix=%s" % tool_inst_dir]) + gcc_build_dir = stage_dir + '/gcc' build_package(gcc_source_dir, gcc_build_dir, ["--prefix=%s" % tool_inst_dir, "--enable-__cxa_atexit", "--with-gmp=%s" % lib_inst_dir, "--with-mpfr=%s" % lib_inst_dir, "--with-mpc=%s" % lib_inst_dir, "--enable-languages=c,c++", @@ -91,54 +99,61 @@ def build_tar_package(tar, name, base, d ############################################## source_dir = os.path.realpath('src') def build_source_dir(prefix, version): return source_dir + '/' + prefix + version binutils_version = "2.21.1" +glibc_version = "2.13" #FIXME: should probably use 2.5.1 tar_version = "1.26" gcc_version = "4.5.2" mpfr_version = "2.4.2" gmp_version = "5.0.1" mpc_version = "0.8.1" binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \ binutils_version +glibc_source_uri = "http://ftp.gnu.org/gnu/glibc/glibc-%s.tar.bz2" % \ + glibc_version tar_source_uri = "http://ftp.gnu.org/gnu/tar/tar-%s.tar.bz2" % \ tar_version gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \ (gcc_version, gcc_version) mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \ (mpfr_version, mpfr_version) gmp_source_uri = "http://ftp.gnu.org/gnu/gmp/gmp-%s.tar.bz2" % gmp_version mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \ mpc_version binutils_source_tar = download_uri(binutils_source_uri) +glibc_source_tar = download_uri(glibc_source_uri) tar_source_tar = download_uri(tar_source_uri) mpc_source_tar = download_uri(mpc_source_uri) mpfr_source_tar = download_uri(mpfr_source_uri) gmp_source_tar = download_uri(gmp_source_uri) gcc_source_tar = download_uri(gcc_source_uri) build_dir = os.path.realpath('build') binutils_source_dir = build_source_dir('binutils-', binutils_version) +glibc_source_dir = build_source_dir('glibc-', glibc_version) tar_source_dir = build_source_dir('tar-', tar_version) mpc_source_dir = build_source_dir('mpc-', mpc_version) mpfr_source_dir = build_source_dir('mpfr-', mpfr_version) gmp_source_dir = build_source_dir('gmp-', gmp_version) gcc_source_dir = build_source_dir('gcc-', gcc_version) if not os.path.exists(source_dir): os.mkdir(source_dir) extract(binutils_source_tar, source_dir) patch('binutils-deterministic.patch', 1, binutils_source_dir) + extract(glibc_source_tar, source_dir) + patch('glibc-deterministic.patch', 1, glibc_source_dir) extract(tar_source_tar, source_dir) extract(mpc_source_tar, source_dir) extract(mpfr_source_tar, source_dir) extract(gmp_source_tar, source_dir) extract(gcc_source_tar, source_dir) patch('plugin_finish_decl.diff', 0, gcc_source_dir) patch('pr49911.diff', 1, gcc_source_dir) patch('r159628-r163231-r171807.patch', 1, gcc_source_dir) @@ -153,12 +168,13 @@ build_tar(build_dir, tar_inst_dir) stage1_dir = build_dir + '/stage1' build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir) stage1_tool_inst_dir = stage1_dir + '/inst' stage2_dir = build_dir + '/stage2' build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc", "CXX" : stage1_tool_inst_dir + "/bin/g++", "AR" : stage1_tool_inst_dir + "/bin/ar", - "RANLIB" : "true" }) + "RANLIB" : "true" }, + stage2_dir) build_tar_package(tar_inst_dir + "/bin/tar", "toolchain.tar", stage2_dir, "inst")
--- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -143,16 +143,17 @@ MOZ_UPDATER = @MOZ_UPDATER@ MOZ_UPDATE_CHANNEL = @MOZ_UPDATE_CHANNEL@ MOZ_UPDATE_PACKAGING = @MOZ_UPDATE_PACKAGING@ MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@ NS_ENABLE_TSF = @NS_ENABLE_TSF@ MOZ_SPELLCHECK = @MOZ_SPELLCHECK@ MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@ MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@ MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@ +MOZ_ONLY_TOUCH_EVENTS = @MOZ_ONLY_TOUCH_EVENTS@ MOZ_TOUCH = @MOZ_TOUCH@ MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@ MOZ_FEEDS = @MOZ_FEEDS@ MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@ MOZ_PLACES = @MOZ_PLACES@ MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@ MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@ MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
--- a/config/expandlibs.py +++ b/config/expandlibs.py @@ -81,16 +81,21 @@ def relativize(path): del abspath[0] if not curdir and not abspath: return '.' relpath = os.path.join(*[os.pardir for i in curdir] + abspath) if len(path) > len(relpath): return relpath return path +def isObject(path): + '''Returns whether the given path points to an object file, that is, + ends with OBJ_SUFFIX or .i_o''' + return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o'] + class LibDescriptor(dict): KEYS = ['OBJS', 'LIBS'] def __init__(self, content=None): '''Creates an instance of a lib descriptor, initialized with contents from a list of strings when given. This is intended for use with file.readlines()''' if isinstance(content, list) and all([isinstance(item, str) for item in content]):
--- a/config/expandlibs_exec.py +++ b/config/expandlibs_exec.py @@ -51,17 +51,17 @@ See https://bugzilla.mozilla.org/show_bu With the --reorder argument, followed by a file name, it will reorder the object files from the command line according to the order given in the file. Implies --extract. ''' from __future__ import with_statement import sys import os -from expandlibs import ExpandArgs, relativize +from expandlibs import ExpandArgs, relativize, isObject import expandlibs_config as conf from optparse import OptionParser import subprocess import tempfile import shutil class ExpandArgsMore(ExpandArgs): ''' Meant to be used as 'with ExpandArgsMore(args) as ...: ''' @@ -92,29 +92,29 @@ class ExpandArgsMore(ExpandArgs): if os.path.exists(arg + conf.LIBS_DESC_SUFFIX): newlist += self._extract(self._expand_desc(arg)) elif os.path.exists(arg) and len(ar_extract): tmp = tempfile.mkdtemp(dir=os.curdir) self.tmp.append(tmp) subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp) objs = [] for root, dirs, files in os.walk(tmp): - objs += [relativize(os.path.join(root, f)) for f in files if os.path.splitext(f)[1] in [conf.OBJ_SUFFIX, '.i_o']] + objs += [relativize(os.path.join(root, f)) for f in files if isObject(f)] newlist += objs else: newlist += [arg] else: newlist += [arg] return newlist def makelist(self): '''Replaces object file names with a temporary list file, using a list format depending on the EXPAND_LIBS_LIST_STYLE variable ''' - objs = [o for o in self if os.path.splitext(o)[1] == conf.OBJ_SUFFIX] + objs = [o for o in self if isObject(o)] if not len(objs): return fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir) if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript": content = ["INPUT(%s)\n" % obj for obj in objs] ref = tmp elif conf.EXPAND_LIBS_LIST_STYLE == "list": content = ["%s\n" % obj for obj in objs] ref = "@" + tmp @@ -129,25 +129,25 @@ class ExpandArgsMore(ExpandArgs): newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs] self[0:] = newlist def reorder(self, order_list): '''Given a list of file names without OBJ_SUFFIX, rearrange self so that the object file names it contains are ordered according to that list. ''' - objs = [o for o in self if o.endswith(conf.OBJ_SUFFIX)] + objs = [o for o in self if isObject(o)] if not objs: return idx = self.index(objs[0]) # Keep everything before the first object, then the ordered objects, # then any other objects, then any non-objects after the first object objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs]) self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \ [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \ - [x for x in self[idx:] if not x.endswith(conf.OBJ_SUFFIX)] + [x for x in self[idx:] if not isObject(x)] def main(): parser = OptionParser() parser.add_option("--extract", action="store_true", dest="extract", help="when a library has no descriptor file, extract it first, when possible") parser.add_option("--uselist", action="store_true", dest="uselist", help="use a list file for objects when executing a command")
--- a/config/expandlibs_gen.py +++ b/config/expandlibs_gen.py @@ -36,22 +36,22 @@ # ***** END LICENSE BLOCK ***** '''Given a list of object files and library names, prints a library descriptor to standard output''' import sys import os import expandlibs_config as conf -from expandlibs import LibDescriptor +from expandlibs import LibDescriptor, isObject def generate(args): desc = LibDescriptor() for arg in args: - if os.path.splitext(arg)[1] in [conf.OBJ_SUFFIX, '.i_o']: + if isObject(arg): desc['OBJS'].append(os.path.abspath(arg)) elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX and \ (os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX)): desc['LIBS'].append(os.path.abspath(arg)) return desc if __name__ == '__main__': print generate(sys.argv[1:])
--- a/configure.in +++ b/configure.in @@ -124,17 +124,17 @@ GTK2_VERSION=2.10.0 WINDRES_VERSION=2.14.90 W32API_VERSION=3.14 GNOMEVFS_VERSION=2.0 GNOMEUI_VERSION=2.2.0 GCONF_VERSION=1.2.1 GIO_VERSION=2.18 STARTUP_NOTIFICATION_VERSION=0.8 DBUS_VERSION=0.60 -SQLITE_VERSION=3.7.7.1 +SQLITE_VERSION=3.7.10 LIBNOTIFY_VERSION=0.4 MSMANIFEST_TOOL= dnl Set various checks dnl ======================================================== MISSING_X= AC_PROG_AWK @@ -1819,19 +1819,33 @@ if test "$OS_TARGET" = "Android"; then AC_MSG_ERROR([Couldn't find path to stlport in the android ndk]) fi CPPFLAGS="$CPPFLAGS $STLPORT_CPPFLAGS" LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS" LIBS="$LIBS $STLPORT_LIBS" fi dnl ======================================================== +dnl Suppress Clang Argument Warnings +dnl ======================================================== +if test -n "$CLANG_CC"; then + _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}" + CPPFLAGS="-Qunused-arguments ${CPPFLAGS}" +fi +if test -n "$CLANG_CXX"; then + _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}" +fi + +dnl ======================================================== dnl GNU specific defaults dnl ======================================================== if test "$GNU_CC"; then + # Per bug 719659 comment 2, some of the headers on ancient build machines + # require gnu89 inline semantics. But otherwise, we use C99. + CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline" # FIXME: Let us build with strict aliasing. bug 414641. CFLAGS="$CFLAGS -fno-strict-aliasing" MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' DSO_LDOPTS='-shared' if test "$GCC_USE_GNU_LD"; then # Don't allow undefined symbols in libraries DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1447,23 +1447,16 @@ public: /** * Increases the count of blockers preventing scripts from running. * NOTE: You might want to use nsAutoScriptBlocker rather than calling * this directly */ static void AddScriptBlocker(); /** - * Increases the count of blockers preventing scripts from running. - * Also, while this script blocker is active, script runners must not be - * added --- we'll assert if one is, and ignore it. - */ - static void AddScriptBlockerAndPreventAddingRunners(); - - /** * Decreases the count of blockers preventing scripts from running. * NOTE: You might want to use nsAutoScriptBlocker rather than calling * this directly * * WARNING! Calling this function could synchronously execute scripts. */ static void RemoveScriptBlocker();
--- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -1595,16 +1595,18 @@ public: enum DeprecatedOperations { #include "nsDeprecatedOperationList.h" eDeprecatedOperationCount }; #undef DEPRECATED_OPERATION void WarnOnceAbout(DeprecatedOperations aOperation); virtual void PostVisibilityUpdateEvent() = 0; + + bool IsSyntheticDocument() { return mIsSyntheticDocument; } void SetNeedLayoutFlush() { mNeedLayoutFlush = true; if (mDisplayDocument) { mDisplayDocument->SetNeedLayoutFlush(); } }
--- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -374,16 +374,21 @@ public: /** * Return this node as an Element. Should only be used for nodes * for which IsElement() is true. */ mozilla::dom::Element* AsElement(); /** + * Return if this node has any children. + */ + bool HasChildren() const { return !!mFirstChild; } + + /** * Get the number of children * @return the number of children */ virtual PRUint32 GetChildCount() const = 0; /** * Get a child by index * @param aIndex the index of the child to get
--- a/content/base/public/nsISelectionController.idl +++ b/content/base/public/nsISelectionController.idl @@ -1,11 +1,10 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, @@ -42,21 +41,22 @@ #include "nsISelection.idl" #include "nsISelectionDisplay.idl" %{C++ typedef short SelectionType; typedef short SelectionRegion; %} +interface nsIContent; interface nsIDOMNode; interface nsISelection; interface nsISelectionDisplay; -[scriptable, uuid(cf30315f-b65d-44c3-8c57-557e36d18fd2)] +[scriptable, uuid(b1ff7faa-8097-431d-b7f1-b0615e3cd596)] interface nsISelectionController : nsISelectionDisplay { const short SELECTION_NONE=0; const short SELECTION_NORMAL=1; const short SELECTION_SPELLCHECK=2; const short SELECTION_IME_RAWINPUT=4; const short SELECTION_IME_SELECTEDRAWTEXT=8; const short SELECTION_IME_CONVERTEDTEXT=16; @@ -271,15 +271,16 @@ interface nsISelectionController : nsISe /** CheckVisibility will return true if textnode and offsets are actually rendered * in the current precontext. * @param aNode textNode to test * @param aStartOffset offset in dom to first char of textnode to test * @param aEndOffset offset in dom to last char of textnode to test * @param aReturnBool boolean returned TRUE if visible FALSE if not */ boolean checkVisibility(in nsIDOMNode node, in short startOffset, in short endOffset); + [noscript,nostdcall] boolean checkVisibilityContent(in nsIContent node, in short startOffset, in short endOffset); }; %{ C++ #define NS_ISELECTIONCONTROLLER_CID \ { 0x513b9460, 0xd56a, 0x4c4e, \ { 0xb6, 0xf9, 0x0b, 0x8a, 0xe4, 0x37, 0x2a, 0x3b }} %}
--- a/content/base/src/Link.h +++ b/content/base/src/Link.h @@ -46,18 +46,18 @@ #include "mozilla/dom/Element.h" #include "mozilla/IHistory.h" namespace mozilla { namespace dom { #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID \ - { 0xa687a99c, 0x3893, 0x45c0, \ - {0x8e, 0xab, 0xb8, 0xf7, 0xd7, 0x9e, 0x9e, 0x7b } } + { 0x7EA57721, 0xE373, 0x458E, \ + {0x8F, 0x44, 0xF8, 0x96, 0x56, 0xB4, 0x14, 0xF5 } } class Link : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID) static const nsLinkState defaultState = eLinkState_Unknown; @@ -108,16 +108,34 @@ public: * true if ResetLinkState should notify the owning document about style * changes or false if it should not. */ void ResetLinkState(bool aNotify); // This method nevers returns a null element. Element* GetElement() const { return mElement; } + /** + * DNS prefetch has been deferred until later, e.g. page load complete. + */ + virtual void OnDNSPrefetchDeferred() { /*do nothing*/ } + + /** + * DNS prefetch has been submitted to Host Resolver. + */ + virtual void OnDNSPrefetchRequested() { /*do nothing*/ } + + /** + * Checks if DNS Prefetching is ok + * + * @returns boolean + * Defaults to true; should be overridden for specialised cases + */ + virtual bool HasDeferredDNSPrefetchRequest() { return true; } + protected: virtual ~Link(); bool HasCachedURI() const { return !!mCachedURI; } private: /** * Unregisters from History so this node no longer gets notifications about
--- a/content/base/src/mozSanitizingSerializer.cpp +++ b/content/base/src/mozSanitizingSerializer.cpp @@ -312,22 +312,16 @@ mozSanitizingHTMLSerializer::AddLeaf(con eHTMLTags type = (eHTMLTags)aNode.GetNodeType(); const nsAString& text = aNode.GetText(); mParserNode = const_cast<nsIParserNode*>(&aNode); return DoAddLeaf(type, text); } NS_IMETHODIMP -mozSanitizingHTMLSerializer::AddDocTypeDecl(const nsIParserNode& aNode) -{ - return NS_OK; -} - -NS_IMETHODIMP mozSanitizingHTMLSerializer::SetDocumentCharset(nsACString& aCharset) { // No idea, if this works - it isn't invoked by |TestOutput|. Write(NS_LITERAL_STRING("\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=") /* Danger: breaking the line within the string literal, like "foo"\n"bar", breaks win32! */ + nsAdoptingString(escape(NS_ConvertASCIItoUTF16(aCharset))) + NS_LITERAL_STRING("\">\n"));
--- a/content/base/src/mozSanitizingSerializer.h +++ b/content/base/src/mozSanitizingSerializer.h @@ -102,29 +102,24 @@ public: // nsIContentSink NS_IMETHOD WillParse(void) { return NS_OK; } NS_IMETHOD WillInterrupt(void) { return NS_OK; } NS_IMETHOD WillResume(void) { return NS_OK; } NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; } NS_IMETHOD OpenContainer(const nsIParserNode& aNode); NS_IMETHOD CloseContainer(const nsHTMLTag aTag); NS_IMETHOD AddLeaf(const nsIParserNode& aNode); - NS_IMETHOD AddComment(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) - { return NS_OK; } - NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode); virtual void FlushPendingNotifications(mozFlushType aType) { } NS_IMETHOD SetDocumentCharset(nsACString& aCharset); virtual nsISupports *GetTarget() { return nsnull; } // nsIHTMLContentSink NS_IMETHOD OpenHead(); NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn); NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; } - NS_IMETHOD_(bool) IsFormOnStack() { return false; } NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; } NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; } NS_IMETHOD DidProcessTokens(void) { return NS_OK; } NS_IMETHOD WillProcessAToken(void) { return NS_OK; } NS_IMETHOD DidProcessAToken(void) { return NS_OK; } // nsISanitizingHTMLSerializer NS_IMETHOD Initialize(nsAString* aOutString,
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -255,17 +255,16 @@ PRUint32 nsContentUtils::sJSGCThingRootC nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull; #endif PRUint32 nsContentUtils::sScriptBlockerCount = 0; #ifdef DEBUG PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0; #endif nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nsnull; PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0; -PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0; nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull; bool nsContentUtils::sIsHandlingKeyBoardEvent = false; bool nsContentUtils::sAllowXULXBL_for_file = false; nsString* nsContentUtils::sShiftText = nsnull; nsString* nsContentUtils::sControlText = nsnull; nsString* nsContentUtils::sMetaText = nsnull; @@ -4414,33 +4413,20 @@ nsContentUtils::AddScriptBlocker() "Should not already have a count"); sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length(); } ++sScriptBlockerCount; } /* static */ void -nsContentUtils::AddScriptBlockerAndPreventAddingRunners() -{ - AddScriptBlocker(); - if (sScriptBlockerCountWhereRunnersPrevented == 0) { - sScriptBlockerCountWhereRunnersPrevented = sScriptBlockerCount; - } -} - -/* static */ -void nsContentUtils::RemoveScriptBlocker() { NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers"); --sScriptBlockerCount; - if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) { - sScriptBlockerCountWhereRunnersPrevented = 0; - } if (sScriptBlockerCount) { return; } PRUint32 firstBlocker = sRunnersCountAtFirstBlocker; PRUint32 lastBlocker = sBlockedScriptRunners->Length(); PRUint32 originalFirstBlocker = firstBlocker; PRUint32 blockersCount = lastBlocker - firstBlocker; @@ -4464,20 +4450,16 @@ nsContentUtils::RemoveScriptBlocker() bool nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) { if (!aRunnable) { return false; } if (sScriptBlockerCount) { - if (sScriptBlockerCountWhereRunnersPrevented > 0) { - NS_ERROR("Adding a script runner when that is prevented!"); - return false; - } return sBlockedScriptRunners->AppendElement(aRunnable) != nsnull; } nsCOMPtr<nsIRunnable> run = aRunnable; run->Run(); return true; } @@ -5449,18 +5431,19 @@ public: { } NS_IMETHOD_(void) NoteRoot(PRUint32 langID, void* root, nsCycleCollectionParticipant* helper) { } NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void* child) { - if (langID == nsIProgrammingLanguage::JAVASCRIPT) { - mFound = child == mWrapper; + if (langID == nsIProgrammingLanguage::JAVASCRIPT && + child == mWrapper) { + mFound = true; } } NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child) { } NS_IMETHOD_(void) NoteNativeChild(void* child, nsCycleCollectionParticipant* helper) {
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -7092,18 +7092,26 @@ nsDocument::BlockOnload() // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup // -- it's not ours. if (mOnloadBlockCount == 0 && mScriptGlobalObject) { if (!nsContentUtils::IsSafeToRunScript()) { // Because AddRequest may lead to OnStateChange calls in chrome, // block onload only when there are no script blockers. ++mAsyncOnloadBlockCount; if (mAsyncOnloadBlockCount == 1) { - nsContentUtils::AddScriptRunner( + bool success = nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload)); + + // The script runner shouldn't fail to add. But if somebody broke + // something and it does, we'll thrash at 100% cpu forever. The best + // response is just to ignore the onload blocking request. See bug 579535. + if (!success) { + NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!"); + mAsyncOnloadBlockCount = 0; + } } return; } nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); if (loadGroup) { loadGroup->AddRequest(mOnloadBlocker, nsnull); } }
--- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -92,16 +92,17 @@ #include "nsFrameLoader.h" #include "nsObjectLoadingContent.h" #include "mozAutoDocUpdate.h" #include "nsIContentSecurityPolicy.h" #include "nsIChannelPolicy.h" #include "nsChannelPolicy.h" #include "mozilla/dom/Element.h" +#include "sampler.h" #ifdef PR_LOGGING static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc"); #endif #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG) @@ -504,16 +505,17 @@ nsObjectLoadingContent::~nsObjectLoading } } // nsIRequestObserver NS_IMETHODIMP nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) { + SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest"); if (aRequest != mChannel || !aRequest) { // This is a bit of an edge case - happens when a new load starts before the // previous one got here return NS_BINDING_ABORTED; } AutoNotifier notifier(this, true);
--- a/content/base/src/nsPlainTextSerializer.h +++ b/content/base/src/nsPlainTextSerializer.h @@ -103,28 +103,24 @@ public: // nsIContentSink NS_IMETHOD WillParse(void) { return NS_OK; } NS_IMETHOD WillInterrupt(void) { return NS_OK; } NS_IMETHOD WillResume(void) { return NS_OK; } NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; } NS_IMETHOD OpenContainer(const nsIParserNode& aNode); NS_IMETHOD CloseContainer(const nsHTMLTag aTag); NS_IMETHOD AddLeaf(const nsIParserNode& aNode); - NS_IMETHOD AddComment(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode) { return NS_OK; } virtual void FlushPendingNotifications(mozFlushType aType) { } NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; } virtual nsISupports *GetTarget() { return nsnull; } // nsIHTMLContentSink NS_IMETHOD OpenHead(); NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn); NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; } - NS_IMETHOD_(bool) IsFormOnStack() { return false; } NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; } NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; } NS_IMETHOD DidProcessTokens(void) { return NS_OK; } NS_IMETHOD WillProcessAToken(void) { return NS_OK; } NS_IMETHOD DidProcessAToken(void) { return NS_OK; } // nsIHTMLToTextSink
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -1815,16 +1815,17 @@ IsSameOrBaseChannel(nsIRequest* aPossibl return aPossibleBase == aChannel; } /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */ NS_IMETHODIMP nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { + SAMPLE_LABEL("nsXMLHttpRequest", "OnStartRequest"); nsresult rv = NS_OK; if (!mFirstStartRequestSeen && mRequestObserver) { mFirstStartRequestSeen = true; mRequestObserver->OnStartRequest(request, ctxt); } if (!IsSameOrBaseChannel(request, mChannel)) { return NS_OK;
--- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -216,17 +216,16 @@ NS_NewCanvasRenderingContextWebGL(nsIDOM NS_ADDREF(*aResult = ctx); return NS_OK; } WebGLContext::WebGLContext() : mCanvasElement(nsnull), gl(nsnull) { - mWidth = mHeight = 0; mGeneration = 0; mInvalidated = false; mResetLayer = true; mVerbose = false; mOptionsFrozen = false; mActiveTexture = 0; mWebGLError = LOCAL_GL_NO_ERROR; @@ -1100,16 +1099,27 @@ WebGLContext::EnsureBackbufferClearedAsN ForceClearFramebufferWithDefaultValues(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT, nsIntRect(0, 0, mWidth, mHeight)); Invalidate(); } +nsresult +WebGLContext::DummyFramebufferOperation(const char *info) +{ + WebGLenum status; + CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER, &status); + if (status == LOCAL_GL_FRAMEBUFFER_COMPLETE) + return NS_OK; + else + return ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info); +} + // We use this timer for many things. Here are the things that it is activated for: // 1) If a script is using the MOZ_WEBGL_lose_context extension. // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the // CONTEXT_LOST_WEBGL error has been triggered. // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the // GPU periodically to see if the reset status bit has been set. // In all of these situations, we use this timer to send the script context lost // and restored events asynchronously. For example, if it triggers a context loss,
--- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -92,16 +92,17 @@ class WebGLBuffer; class WebGLProgram; class WebGLShader; class WebGLFramebuffer; class WebGLRenderbuffer; class WebGLUniformLocation; class WebGLExtension; struct WebGLVertexAttribData; +class WebGLRectangleObject; class WebGLContextBoundObject; enum FakeBlackStatus { DoNotNeedFakeBlack, DoNeedFakeBlack, DontKnowIfNeedFakeBlack }; struct VertexAttrib0Status { enum { Default, EmulatedUninitializedArray, EmulatedInitializedArray }; }; @@ -430,16 +431,57 @@ private: NS_RUNTIMEABORT("ran out of monotonic ids!"); return mCurrentMonotonicHandle.value(); } nsTArray<Entry> mArray; CheckedInt<WebGLMonotonicHandle> mCurrentMonotonicHandle; }; +// this class is a mixin for GL objects that have dimensions +// that we need to track. +class WebGLRectangleObject +{ +public: + WebGLRectangleObject() + : mWidth(0), mHeight(0) { } + + WebGLRectangleObject(WebGLsizei width, WebGLsizei height) + : mWidth(width), mHeight(height) { } + + WebGLsizei Width() const { return mWidth; } + void width(WebGLsizei value) { mWidth = value; } + + WebGLsizei Height() const { return mHeight; } + void height(WebGLsizei value) { mHeight = value; } + + void setDimensions(WebGLsizei width, WebGLsizei height) { + mWidth = width; + mHeight = height; + } + + void setDimensions(WebGLRectangleObject *rect) { + if (rect) { + mWidth = rect->Width(); + mHeight = rect->Height(); + } else { + mWidth = 0; + mHeight = 0; + } + } + + bool HasSameDimensionsAs(const WebGLRectangleObject& other) const { + return Width() == other.Width() && Height() == other.Height(); + } + +protected: + WebGLsizei mWidth; + WebGLsizei mHeight; +}; + struct WebGLContextOptions { // these are defaults WebGLContextOptions() : alpha(true), depth(true), stencil(false), premultipliedAlpha(true), antialias(true), preserveDrawingBuffer(false) { } @@ -464,17 +506,18 @@ struct WebGLContextOptions { bool antialias; bool preserveDrawingBuffer; }; class WebGLContext : public nsIDOMWebGLRenderingContext, public nsICanvasRenderingContextInternal, public nsSupportsWeakReference, - public nsITimerCallback + public nsITimerCallback, + public WebGLRectangleObject { friend class WebGLMemoryReporter; friend class WebGLExtensionLoseContext; friend class WebGLContextUserData; public: WebGLContext(); virtual ~WebGLContext(); @@ -518,37 +561,42 @@ public: bool RestoreContext(); nsresult SynthesizeGLError(WebGLenum err); nsresult SynthesizeGLError(WebGLenum err, const char *fmt, ...); nsresult ErrorInvalidEnum(const char *fmt = 0, ...); nsresult ErrorInvalidOperation(const char *fmt = 0, ...); nsresult ErrorInvalidValue(const char *fmt = 0, ...); + nsresult ErrorInvalidFramebufferOperation(const char *fmt = 0, ...); nsresult ErrorInvalidEnumInfo(const char *info, PRUint32 enumvalue) { return ErrorInvalidEnum("%s: invalid enum value 0x%x", info, enumvalue); } nsresult ErrorOutOfMemory(const char *fmt = 0, ...); - + const char *ErrorName(GLenum error); + nsresult DummyFramebufferOperation(const char *info); + WebGLTexture *activeBoundTextureForTarget(WebGLenum target) { return target == LOCAL_GL_TEXTURE_2D ? mBound2DTextures[mActiveTexture] : mBoundCubeMapTextures[mActiveTexture]; } already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder, CanvasLayer *aOldLayer, LayerManager *aManager); void MarkContextClean() { mInvalidated = false; } // a number that increments every time we have an event that causes // all context resources to be lost. PRUint32 Generation() { return mGeneration.value(); } + const WebGLRectangleObject *FramebufferRectangleObject() const; + // this is similar to GLContext::ClearSafely, but is more comprehensive // (takes care of scissor, stencil write mask, dithering, viewport...) // WebGL has more complex needs than GLContext as content controls GL state. void ForceClearFramebufferWithDefaultValues(PRUint32 mask, const nsIntRect& viewportRect); // if the preserveDrawingBuffer context option is false, we need to clear the back buffer // after it's been presented to the compositor. This function does that if needed. // See section 2.2 in the WebGL spec. @@ -637,17 +685,16 @@ protected: nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement; nsHTMLCanvasElement *HTMLCanvasElement() { return static_cast<nsHTMLCanvasElement*>(mCanvasElement.get()); } nsRefPtr<gl::GLContext> gl; - PRInt32 mWidth, mHeight; CheckedUint32 mGeneration; WebGLContextOptions mOptions; bool mInvalidated; bool mResetLayer; bool mVerbose; bool mOptionsFrozen; @@ -906,55 +953,16 @@ public: friend class WebGLFramebuffer; friend class WebGLRenderbuffer; friend class WebGLProgram; friend class WebGLBuffer; friend class WebGLShader; friend class WebGLUniformLocation; }; -// this class is a mixin for GL objects that have dimensions -// that we need to track. -class WebGLRectangleObject -{ -protected: - WebGLRectangleObject() - : mWidth(0), mHeight(0) { } - -public: - WebGLsizei width() const { return mWidth; } - void width(WebGLsizei value) { mWidth = value; } - - WebGLsizei height() const { return mHeight; } - void height(WebGLsizei value) { mHeight = value; } - - void setDimensions(WebGLsizei width, WebGLsizei height) { - mWidth = width; - mHeight = height; - } - - void setDimensions(WebGLRectangleObject *rect) { - if (rect) { - mWidth = rect->width(); - mHeight = rect->height(); - } else { - mWidth = 0; - mHeight = 0; - } - } - - bool HasSameDimensionsAs(const WebGLRectangleObject& other) const { - return width() == other.width() && height() == other.height(); - } - -protected: - WebGLsizei mWidth; - WebGLsizei mHeight; -}; - // This class is a mixin for objects that are tied to a specific // context (which is to say, all of them). They provide initialization // as well as comparison with the current context. class WebGLContextBoundObject { public: WebGLContextBoundObject(WebGLContext *context) { mContext = context; @@ -1201,21 +1209,22 @@ protected: bool mHasEverBeenBound; WebGLuint mGLName; // we store information about the various images that are part of // this texture (cubemap faces, mipmap levels) public: - struct ImageInfo { - ImageInfo() : mWidth(0), mHeight(0), mFormat(0), mType(0), mIsDefined(false) {} + class ImageInfo : public WebGLRectangleObject { + public: + ImageInfo() : mFormat(0), mType(0), mIsDefined(false) {} ImageInfo(WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type) - : mWidth(width), mHeight(height), mFormat(format), mType(type), mIsDefined(true) {} + : WebGLRectangleObject(width, height), mFormat(format), mType(type), mIsDefined(true) {} bool operator==(const ImageInfo& a) const { return mWidth == a.mWidth && mHeight == a.mHeight && mFormat == a.mFormat && mType == a.mType; } bool operator!=(const ImageInfo& a) const { return !(*this == a); } @@ -1230,19 +1239,23 @@ public: is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...) } PRInt64 MemoryUsage() const { if (!mIsDefined) return 0; PRInt64 texelSize = WebGLContext::GetTexelSize(mFormat, mType); return PRInt64(mWidth) * PRInt64(mHeight) * texelSize; } - WebGLsizei mWidth, mHeight; + WebGLenum Format() const { return mFormat; } + WebGLenum Type() const { return mType; } + protected: WebGLenum mFormat, mType; bool mIsDefined; + + friend class WebGLTexture; }; ImageInfo& ImageInfoAt(size_t level, size_t face = 0) { #ifdef DEBUG if (face >= mFacesCount) NS_ERROR("wrong face index, must be 0 for TEXTURE_2D and at most 5 for cube maps"); #endif // no need to check level as a wrong value would be caught by ElementAt(). @@ -1821,17 +1834,17 @@ public: WebGLenum InternalFormat() const { return mInternalFormat; } void SetInternalFormat(WebGLenum aInternalFormat) { mInternalFormat = aInternalFormat; } WebGLenum InternalFormatForGL() const { return mInternalFormatForGL; } void SetInternalFormatForGL(WebGLenum aInternalFormatForGL) { mInternalFormatForGL = aInternalFormatForGL; } PRInt64 MemoryUsage() const { - PRInt64 pixels = PRInt64(width()) * PRInt64(height()); + PRInt64 pixels = PRInt64(Width()) * PRInt64(Height()); switch (mInternalFormatForGL) { case LOCAL_GL_STENCIL_INDEX8: return pixels; case LOCAL_GL_RGBA4: case LOCAL_GL_RGB5_A1: case LOCAL_GL_RGB565: case LOCAL_GL_DEPTH_COMPONENT16: return 2 * pixels; @@ -1859,17 +1872,16 @@ protected: WebGLMonotonicHandle mMonotonicHandle; bool mHasEverBeenBound; bool mInitialized; friend class WebGLFramebuffer; }; class WebGLFramebufferAttachment - : public WebGLRectangleObject { // deleting a texture or renderbuffer immediately detaches it WebGLRefPtr<WebGLTexture> mTexturePtr; WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr; WebGLenum mAttachmentPoint; WebGLint mTextureLevel; WebGLenum mTextureCubeMapFace; @@ -1885,90 +1897,113 @@ public: bool IsDeleteRequested() const { return Texture() ? Texture()->IsDeleteRequested() : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() : false; } bool HasAlpha() const { WebGLenum format = 0; - if (Texture() && Texture()->HasImageInfoAt(0,0)) - format = mTexturePtr->ImageInfoAt(0,0).mFormat; + if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) + format = Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace).Format(); else if (Renderbuffer()) - format = mRenderbufferPtr->InternalFormat(); + format = Renderbuffer()->InternalFormat(); return format == LOCAL_GL_RGBA || format == LOCAL_GL_LUMINANCE_ALPHA || format == LOCAL_GL_ALPHA || format == LOCAL_GL_RGBA4 || format == LOCAL_GL_RGB5_A1; } void SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face) { mTexturePtr = tex; mRenderbufferPtr = nsnull; mTextureLevel = level; mTextureCubeMapFace = face; - if (tex) { - const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(level, face); - setDimensions(imageInfo.mWidth, imageInfo.mHeight); - } else { - setDimensions(0, 0); - } } void SetRenderbuffer(WebGLRenderbuffer *rb) { mTexturePtr = nsnull; mRenderbufferPtr = rb; - setDimensions(rb); + } + const WebGLTexture *Texture() const { + return mTexturePtr; } - WebGLTexture *Texture() const { - return mTexturePtr.get(); + WebGLTexture *Texture() { + return mTexturePtr; } - WebGLRenderbuffer *Renderbuffer() const { - return mRenderbufferPtr.get(); + const WebGLRenderbuffer *Renderbuffer() const { + return mRenderbufferPtr; + } + WebGLRenderbuffer *Renderbuffer() { + return mRenderbufferPtr; } WebGLint TextureLevel() const { return mTextureLevel; } WebGLenum TextureCubeMapFace() const { return mTextureCubeMapFace; } - bool IsIncompatibleWithAttachmentPoint() const - { - // textures can only be color textures in WebGL - if (mTexturePtr) - return mAttachmentPoint != LOCAL_GL_COLOR_ATTACHMENT0; - - if (mRenderbufferPtr) { - WebGLenum format = mRenderbufferPtr->InternalFormat(); - switch (mAttachmentPoint) { - case LOCAL_GL_COLOR_ATTACHMENT0: - return format != LOCAL_GL_RGB565 && - format != LOCAL_GL_RGB5_A1 && - format != LOCAL_GL_RGBA4; - case LOCAL_GL_DEPTH_ATTACHMENT: - return format != LOCAL_GL_DEPTH_COMPONENT16; - case LOCAL_GL_STENCIL_ATTACHMENT: - return format != LOCAL_GL_STENCIL_INDEX8; - case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - return format != LOCAL_GL_DEPTH_STENCIL; - } - } - - return false; // no attachment at all, so no incompatibility - } - bool HasUninitializedRenderbuffer() const { return mRenderbufferPtr && !mRenderbufferPtr->Initialized(); } void Reset() { mTexturePtr = nsnull; mRenderbufferPtr = nsnull; } + + const WebGLRectangleObject* RectangleObject() const { + if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) + return &Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace); + else if (Renderbuffer()) + return Renderbuffer(); + else + return nsnull; + } + bool HasSameDimensionsAs(const WebGLFramebufferAttachment& other) const { + const WebGLRectangleObject *thisRect = RectangleObject(); + const WebGLRectangleObject *otherRect = other.RectangleObject(); + return thisRect && + otherRect && + thisRect->HasSameDimensionsAs(*otherRect); + } + + bool IsComplete() const { + const WebGLRectangleObject *thisRect = RectangleObject(); + + if (!thisRect || + !thisRect->Width() || + !thisRect->Height()) + return false; + + if (mTexturePtr) + return mAttachmentPoint == LOCAL_GL_COLOR_ATTACHMENT0; + + if (mRenderbufferPtr) { + WebGLenum format = mRenderbufferPtr->InternalFormat(); + switch (mAttachmentPoint) { + case LOCAL_GL_COLOR_ATTACHMENT0: + return format == LOCAL_GL_RGB565 || + format == LOCAL_GL_RGB5_A1 || + format == LOCAL_GL_RGBA4; + case LOCAL_GL_DEPTH_ATTACHMENT: + return format == LOCAL_GL_DEPTH_COMPONENT16; + case LOCAL_GL_STENCIL_ATTACHMENT: + return format == LOCAL_GL_STENCIL_INDEX8; + case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: + return format == LOCAL_GL_DEPTH_STENCIL; + default: + NS_ABORT(); // should have been validated earlier + } + } + + NS_ABORT(); // should never get there + return false; + } }; class WebGLFramebuffer : public nsIWebGLFramebuffer , public WebGLRefCountedObject<WebGLFramebuffer> , public WebGLContextBoundObject { public: @@ -1998,19 +2033,16 @@ public: mContext->gl->fDeleteFramebuffers(1, &mGLName); mContext->mFramebuffers.RemoveElement(mMonotonicHandle); } bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } WebGLuint GLName() { return mGLName; } - WebGLsizei width() { return mColorAttachment.width(); } - WebGLsizei height() { return mColorAttachment.height(); } - nsresult FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, nsIWebGLRenderbuffer *rbobj) { WebGLuint renderbuffername; bool isNull; WebGLRenderbuffer *wrb; @@ -2109,60 +2141,33 @@ public: mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, texturename, level); } else { mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level); } return NS_OK; } - bool CheckAndInitializeRenderbuffers() - { - if (HasBadAttachments()) { - mContext->SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); - return false; - } - - if (mColorAttachment.HasUninitializedRenderbuffer() || - mDepthAttachment.HasUninitializedRenderbuffer() || - mStencilAttachment.HasUninitializedRenderbuffer() || - mDepthStencilAttachment.HasUninitializedRenderbuffer()) - { - InitializeRenderbuffers(); - } - - return true; + bool HasIncompleteAttachment() const { + return (mColorAttachment.IsDefined() && !mColorAttachment.IsComplete()) || + (mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) || + (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) || + (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete()); } - bool HasBadAttachments() const { - if (mColorAttachment.IsIncompatibleWithAttachmentPoint() || - mDepthAttachment.IsIncompatibleWithAttachmentPoint() || - mStencilAttachment.IsIncompatibleWithAttachmentPoint() || - mDepthStencilAttachment.IsIncompatibleWithAttachmentPoint()) - { - // some attachment is incompatible with its attachment point - return true; - } - - if (int(mDepthAttachment.IsDefined()) + - int(mStencilAttachment.IsDefined()) + - int(mDepthStencilAttachment.IsDefined()) >= 2) - { - // has at least two among Depth, Stencil, DepthStencil - return true; - } - - if (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) - return true; - if (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) - return true; - if (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment)) - return true; - - return false; + bool HasDepthStencilConflict() const { + return int(mDepthAttachment.IsDefined()) + + int(mStencilAttachment.IsDefined()) + + int(mDepthStencilAttachment.IsDefined()) >= 2; + } + + bool HasAttachmentsOfMismatchedDimensions() const { + return (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) || + (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) || + (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment)); } const WebGLFramebufferAttachment& ColorAttachment() const { return mColorAttachment; } const WebGLFramebufferAttachment& DepthAttachment() const { return mDepthAttachment; @@ -2205,28 +2210,49 @@ public: if (mDepthAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nsnull); if (mStencilAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nsnull); if (mDepthStencilAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nsnull); } + const WebGLRectangleObject *RectangleObject() { + return mColorAttachment.RectangleObject(); + } + NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLFRAMEBUFFER -protected: - - // protected because WebGLContext should only call InitializeRenderbuffers - void InitializeRenderbuffers() + bool CheckAndInitializeRenderbuffers() { + // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not + // generate the INVALID_FRAMEBUFFER_OPERATION that we need here + if (HasDepthStencilConflict()) + return false; + + if (!mColorAttachment.HasUninitializedRenderbuffer() && + !mDepthAttachment.HasUninitializedRenderbuffer() && + !mStencilAttachment.HasUninitializedRenderbuffer() && + !mDepthStencilAttachment.HasUninitializedRenderbuffer()) + return true; + + // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case + const WebGLRectangleObject *rect = mColorAttachment.RectangleObject(); + if (!rect || + !rect->Width() || + !rect->Height()) + return false; + mContext->MakeContextCurrent(); - if (mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE) - return; + WebGLenum status; + mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER, &status); + if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) + return false; PRUint32 mask = 0; if (mColorAttachment.HasUninitializedRenderbuffer()) mask |= LOCAL_GL_COLOR_BUFFER_BIT; if (mDepthAttachment.HasUninitializedRenderbuffer() || mDepthStencilAttachment.HasUninitializedRenderbuffer()) @@ -2235,30 +2261,31 @@ protected: } if (mStencilAttachment.HasUninitializedRenderbuffer() || mDepthStencilAttachment.HasUninitializedRenderbuffer()) { mask |= LOCAL_GL_STENCIL_BUFFER_BIT; } - // the one useful line of code - mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0,0,width(),height())); + mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0, 0, rect->Width(), rect->Height())); if (mColorAttachment.HasUninitializedRenderbuffer()) mColorAttachment.Renderbuffer()->SetInitialized(true); if (mDepthAttachment.HasUninitializedRenderbuffer()) mDepthAttachment.Renderbuffer()->SetInitialized(true); if (mStencilAttachment.HasUninitializedRenderbuffer()) mStencilAttachment.Renderbuffer()->SetInitialized(true); if (mDepthStencilAttachment.HasUninitializedRenderbuffer()) mDepthStencilAttachment.Renderbuffer()->SetInitialized(true); + + return true; } WebGLuint mGLName; bool mHasEverBeenBound; // we only store pointers to attached renderbuffers, not to attached textures, because // we will only need to initialize renderbuffers. Textures are already initialized. WebGLFramebufferAttachment mColorAttachment, @@ -2358,16 +2385,21 @@ public: WebGLExtension(WebGLContext *baseContext) : WebGLContextBoundObject(baseContext) {} NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLEXTENSION }; +inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const { + return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject() + : static_cast<const WebGLRectangleObject*>(this); +} + /** ** Template implementations **/ /* Helper function taking a BaseInterfaceType pointer, casting it to * ConcreteObjectType and performing some checks along the way. * * By default, null (respectively: deleted) aInterface pointers are
--- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -681,18 +681,26 @@ WebGLContext::CheckFramebufferStatus(Web } *retval = 0; MakeContextCurrent(); if (target != LOCAL_GL_FRAMEBUFFER) return ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER"); - if (mBoundFramebuffer && mBoundFramebuffer->HasBadAttachments()) + if (!mBoundFramebuffer) + *retval = LOCAL_GL_FRAMEBUFFER_COMPLETE; + else if(mBoundFramebuffer->HasDepthStencilConflict()) *retval = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; + else if(!mBoundFramebuffer->ColorAttachment().IsDefined()) + *retval = LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + else if(mBoundFramebuffer->HasIncompleteAttachment()) + *retval = LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + else if(mBoundFramebuffer->HasAttachmentsOfMismatchedDimensions()) + *retval = LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; else *retval = gl->fCheckFramebufferStatus(target); return NS_OK; } NS_IMETHODIMP WebGLContext::Clear(PRUint32 mask) @@ -705,17 +713,17 @@ WebGLContext::Clear(PRUint32 mask) PRUint32 m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT); if (mask != m) return ErrorInvalidValue("clear: invalid mask bits"); bool needClearCallHere = true; if (mBoundFramebuffer) { if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) - return NS_OK; + return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer"); } else { // no FBO is bound, so we are clearing the backbuffer here EnsureBackbufferClearedAsNeeded(); bool valuesAreDefault = mColorClearValue[0] == 0.0f && mColorClearValue[1] == 0.0f && mColorClearValue[2] == 0.0f && mColorClearValue[3] == 0.0f && mDepthClearValue == 1.0f && @@ -797,19 +805,19 @@ WebGLContext::CopyTexSubImage2D_base(Web WebGLint xoffset, WebGLint yoffset, WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height, bool sub) { - - WebGLsizei framebufferWidth = mBoundFramebuffer ? mBoundFramebuffer->width() : mWidth; - WebGLsizei framebufferHeight = mBoundFramebuffer ? mBoundFramebuffer->height() : mHeight; + const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject(); + WebGLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0; + WebGLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0; const char *info = sub ? "copyTexSubImage2D" : "copyTexImage2D"; MakeContextCurrent(); if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) { if (sub) gl->fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); @@ -856,17 +864,17 @@ WebGLContext::CopyTexSubImage2D_base(Web // if we are completely outside of the framebuffer, we can exit now with our black texture if ( x >= framebufferWidth || x+width <= 0 || y >= framebufferHeight || y+height <= 0) { // we are completely outside of range, can exit now with buffer filled with zeros - return NS_OK; + return DummyFramebufferOperation(info); } GLint actual_x = clamped(x, 0, framebufferWidth); GLint actual_x_plus_width = clamped(x + width, 0, framebufferWidth); GLsizei actual_width = actual_x_plus_width - actual_x; GLint actual_xoffset = xoffset + actual_x - x; GLint actual_y = clamped(y, 0, framebufferHeight); @@ -941,36 +949,37 @@ WebGLContext::CopyTexImage2D(WebGLenum t internalformat == LOCAL_GL_ALPHA || internalformat == LOCAL_GL_LUMINANCE_ALPHA; bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment().HasAlpha() : bool(gl->ActualFormat().alpha > 0); if (texFormatRequiresAlpha && !fboFormatHasAlpha) return ErrorInvalidOperation("copyTexImage2D: texture format requires an alpha channel " "but the framebuffer doesn't have one"); - if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers()) - return NS_OK; + if (mBoundFramebuffer) + if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) + return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer"); WebGLTexture *tex = activeBoundTextureForTarget(target); if (!tex) return ErrorInvalidOperation("copyTexImage2D: no texture bound to this target"); // copyTexImage2D only generates textures with type = UNSIGNED_BYTE GLenum type = LOCAL_GL_UNSIGNED_BYTE; // check if the memory size of this texture may change with this call bool sizeMayChange = true; size_t face = WebGLTexture::FaceForTarget(target); if (tex->HasImageInfoAt(level, face)) { const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(level, face); - sizeMayChange = width != imageInfo.mWidth || - height != imageInfo.mHeight || - internalformat != imageInfo.mFormat || - type != imageInfo.mType; + sizeMayChange = width != imageInfo.Width() || + height != imageInfo.Height() || + internalformat != imageInfo.Format() || + type != imageInfo.Type(); } if (sizeMayChange) { UpdateWebGLErrorAndClearGLError(); CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false); GLenum error = LOCAL_GL_NO_ERROR; UpdateWebGLErrorAndClearGLError(&error); if (error) { @@ -1027,38 +1036,40 @@ WebGLContext::CopyTexSubImage2D(WebGLenu WebGLTexture *tex = activeBoundTextureForTarget(target); if (!tex) return ErrorInvalidOperation("copyTexSubImage2D: no texture bound to this target"); WebGLint face = WebGLTexture::FaceForTarget(target); if (!tex->HasImageInfoAt(level, face)) return ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face"); - WebGLsizei texWidth = tex->ImageInfoAt(level, face).mWidth; - WebGLsizei texHeight = tex->ImageInfoAt(level, face).mHeight; + const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(level, face); + WebGLsizei texWidth = imageInfo.Width(); + WebGLsizei texHeight = imageInfo.Height(); if (xoffset + width > texWidth || xoffset + width < 0) return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large"); if (yoffset + height > texHeight || yoffset + height < 0) return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large"); - WebGLenum format = tex->ImageInfoAt(level, face).mFormat; + WebGLenum format = imageInfo.Format(); bool texFormatRequiresAlpha = format == LOCAL_GL_RGBA || - format == LOCAL_GL_ALPHA || - format == LOCAL_GL_LUMINANCE_ALPHA; + format == LOCAL_GL_ALPHA || + format == LOCAL_GL_LUMINANCE_ALPHA; bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment().HasAlpha() : bool(gl->ActualFormat().alpha > 0); if (texFormatRequiresAlpha && !fboFormatHasAlpha) return ErrorInvalidOperation("copyTexSubImage2D: texture format requires an alpha channel " "but the framebuffer doesn't have one"); - if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers()) - return NS_OK; + if (mBoundFramebuffer) + if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) + return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer"); return CopyTexSubImage2D_base(target, level, format, xoffset, yoffset, x, y, width, height, true); } NS_IMETHODIMP WebGLContext::CreateProgram(nsIWebGLProgram **retval) { @@ -1582,17 +1593,17 @@ WebGLContext::DrawArrays(GLenum mode, We if (checked_firstPlusCount.value() > maxAllowedCount) return ErrorInvalidOperation("drawArrays: bound vertex attribute buffers do not have sufficient size for given first and count"); MakeContextCurrent(); if (mBoundFramebuffer) { if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) - return NS_OK; + return ErrorInvalidFramebufferOperation("drawArrays: incomplete framebuffer"); } else { EnsureBackbufferClearedAsNeeded(); } BindFakeBlackTextures(); if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) return NS_OK; @@ -1693,17 +1704,17 @@ WebGLContext::DrawElements(WebGLenum mod "size for given indices from the bound element array"); } } MakeContextCurrent(); if (mBoundFramebuffer) { if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) - return NS_OK; + return ErrorInvalidFramebufferOperation("drawElements: incomplete framebuffer"); } else { EnsureBackbufferClearedAsNeeded(); } BindFakeBlackTextures(); if (!DoFakeVertexAttrib0(checked_maxIndexPlusOne.value())) return NS_OK; @@ -2401,30 +2412,30 @@ WebGLContext::GetFramebufferAttachmentPa if (fba.Renderbuffer()) { switch (pname) { case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: wrval->SetAsInt32(LOCAL_GL_RENDERBUFFER); break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - wrval->SetAsISupports(fba.Renderbuffer()); + wrval->SetAsISupports(const_cast<WebGLRenderbuffer*>(fba.Renderbuffer())); break; default: return ErrorInvalidEnumInfo("GetFramebufferAttachmentParameter: pname", pname); } } else if (fba.Texture()) { switch (pname) { case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: wrval->SetAsInt32(LOCAL_GL_TEXTURE); break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - wrval->SetAsISupports(fba.Texture()); + wrval->SetAsISupports(const_cast<WebGLTexture*>(fba.Texture())); break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: wrval->SetAsInt32(fba.TextureLevel()); break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: wrval->SetAsInt32(fba.TextureCubeMapFace()); @@ -3283,18 +3294,19 @@ WebGLContext::ReadPixels_base(WebGLint x } if (width < 0 || height < 0) return ErrorInvalidValue("ReadPixels: negative size passed"); if (!pixels) return ErrorInvalidValue("ReadPixels: null array passed"); - WebGLsizei boundWidth = mBoundFramebuffer ? mBoundFramebuffer->width() : mWidth; - WebGLsizei boundHeight = mBoundFramebuffer ? mBoundFramebuffer->height() : mHeight; + const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject(); + WebGLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0; + WebGLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0; void* data = JS_GetTypedArrayData(pixels); PRUint32 dataByteLen = JS_GetTypedArrayByteLength(pixels); int dataType = JS_GetTypedArrayType(pixels); PRUint32 channels = 0; // Check the format param @@ -3365,57 +3377,57 @@ WebGLContext::ReadPixels_base(WebGLint x return ErrorInvalidOperation("readPixels: Invalid format/type pair"); } MakeContextCurrent(); if (mBoundFramebuffer) { // prevent readback of arbitrary video memory through uninitialized renderbuffers! if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) - return NS_OK; + return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer"); } else { EnsureBackbufferClearedAsNeeded(); } // Now that the errors are out of the way, on to actually reading // If we won't be reading any pixels anyways, just skip the actual reading if (width == 0 || height == 0) - return NS_OK; - - if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, boundWidth, boundHeight)) { + return DummyFramebufferOperation("readPixels"); + + if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) { // the easy case: we're not reading out-of-range pixels gl->fReadPixels(x, y, width, height, format, type, data); } else { // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL // to do that for us, because passing out of range parameters to a buggy OpenGL implementation // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize // the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer // to accomodate the potentially different strides (widths). // zero the whole destination buffer. Too bad for the part that's going to be overwritten, we're not // 100% efficient here, but in practice this is a quite rare case anyway. memset(data, 0, dataByteLen); - if ( x >= boundWidth + if ( x >= framebufferWidth || x+width <= 0 - || y >= boundHeight + || y >= framebufferHeight || y+height <= 0) { // we are completely outside of range, can exit now with buffer filled with zeros - return NS_OK; + return DummyFramebufferOperation("readPixels"); } // compute the parameters of the subrect we're actually going to call glReadPixels on GLint subrect_x = NS_MAX(x, 0); - GLint subrect_end_x = NS_MIN(x+width, boundWidth); + GLint subrect_end_x = NS_MIN(x+width, framebufferWidth); GLsizei subrect_width = subrect_end_x - subrect_x; GLint subrect_y = NS_MAX(y, 0); - GLint subrect_end_y = NS_MIN(y+height, boundHeight); + GLint subrect_end_y = NS_MIN(y+height, framebufferHeight); GLsizei subrect_height = subrect_end_y - subrect_y; if (subrect_width < 0 || subrect_height < 0 || subrect_width > width || subrect_height > height) return ErrorInvalidOperation("ReadPixels: integer overflow computing clipped rect size"); // now we know that subrect_width is in the [0..width] interval, and same for heights. @@ -3552,26 +3564,26 @@ WebGLContext::RenderbufferStorage(WebGLe internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8; break; default: return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat); } MakeContextCurrent(); - bool sizeChanges = width != mBoundRenderbuffer->width() || - height != mBoundRenderbuffer->height() || + bool sizeChanges = width != mBoundRenderbuffer->Width() || + height != mBoundRenderbuffer->Height() || internalformat != mBoundRenderbuffer->InternalFormat(); if (sizeChanges) { UpdateWebGLErrorAndClearGLError(); gl->fRenderbufferStorage(target, internalformatForGL, width, height); GLenum error = LOCAL_GL_NO_ERROR; UpdateWebGLErrorAndClearGLError(&error); if (error) { - LogMessageIfVerbose("bufferData generated error %s", ErrorName(error)); + LogMessageIfVerbose("renderbufferStorage generated error %s", ErrorName(error)); return NS_OK; } } else { gl->fRenderbufferStorage(target, internalformatForGL, width, height); } mBoundRenderbuffer->SetInternalFormat(internalformat); mBoundRenderbuffer->SetInternalFormatForGL(internalformatForGL); @@ -4727,20 +4739,20 @@ GLenum WebGLContext::CheckedTexImage2D(G WebGLTexture *tex = activeBoundTextureForTarget(target); NS_ABORT_IF_FALSE(tex != nsnull, "no texture bound"); bool sizeMayChange = true; size_t face = WebGLTexture::FaceForTarget(target); if (tex->HasImageInfoAt(level, face)) { const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(level, face); - sizeMayChange = width != imageInfo.mWidth || - height != imageInfo.mHeight || - format != imageInfo.mFormat || - type != imageInfo.mType; + sizeMayChange = width != imageInfo.Width() || + height != imageInfo.Height() || + format != imageInfo.Format() || + type != imageInfo.Type(); } if (sizeMayChange) { UpdateWebGLErrorAndClearGLError(); gl->fTexImage2D(target, level, internalFormat, width, height, border, format, type, data); GLenum error = LOCAL_GL_NO_ERROR; UpdateWebGLErrorAndClearGLError(&error); return error; @@ -5034,21 +5046,21 @@ WebGLContext::TexSubImage2D_base(WebGLen return ErrorInvalidOperation("texSubImage2D: no texture is bound to this target"); size_t face = WebGLTexture::FaceForTarget(target); if (!tex->HasImageInfoAt(level, face)) return ErrorInvalidOperation("texSubImage2D: no texture image previously defined for this level and face"); const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(level, face); - if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, imageInfo.mWidth, imageInfo.mHeight)) + if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, imageInfo.Width(), imageInfo.Height())) return ErrorInvalidValue("texSubImage2D: subtexture rectangle out of bounds"); // Require the format and type in texSubImage2D to match that of the existing texture as created by texImage2D - if (imageInfo.mFormat != format || imageInfo.mType != type) + if (imageInfo.Format() != format || imageInfo.Type() != type) return ErrorInvalidOperation("texSubImage2D: format or type doesn't match the existing texture"); MakeContextCurrent(); int dstFormat = GetWebGLTexelFormat(format, type); int actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat; size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
--- a/content/canvas/src/WebGLContextUtils.cpp +++ b/content/canvas/src/WebGLContextUtils.cpp @@ -193,16 +193,27 @@ WebGLContext::ErrorInvalidValue(const ch va_start(va, fmt); LogMessageIfVerbose(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_VALUE); } nsresult +WebGLContext::ErrorInvalidFramebufferOperation(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + LogMessageIfVerbose(fmt, va); + va_end(va); + + return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); +} + +nsresult WebGLContext::ErrorOutOfMemory(const char *fmt, ...) { va_list va; va_start(va, fmt); LogMessageIfVerbose(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY);
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -2535,17 +2535,17 @@ nsCanvasRenderingContext2D::SetFont(cons nsRefPtr<nsStyleContext> sc = styleSet->ResolveStyleForRules(parentContext, rules); if (!sc) return NS_ERROR_FAILURE; const nsStyleFont* fontStyle = sc->GetStyleFont(); NS_ASSERTION(fontStyle, "Could not obtain font style"); - nsIAtom* language = sc->GetStyleVisibility()->mLanguage; + nsIAtom* language = sc->GetStyleFont()->mLanguage; if (!language) { language = presShell->GetPresContext()->GetLanguageFromCharset(); } // use CSS pixels instead of dev pixels to avoid being affected by page zoom const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel(); // un-zoom the font size to avoid being affected by text-only zoom const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mFont.size);
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp @@ -2746,17 +2746,17 @@ nsCanvasRenderingContext2DAzure::SetFont if (!sc) { return NS_ERROR_FAILURE; } const nsStyleFont* fontStyle = sc->GetStyleFont(); NS_ASSERTION(fontStyle, "Could not obtain font style"); - nsIAtom* language = sc->GetStyleVisibility()->mLanguage; + nsIAtom* language = sc->GetStyleFont()->mLanguage; if (!language) { language = presShell->GetPresContext()->GetLanguageFromCharset(); } // use CSS pixels instead of dev pixels to avoid being affected by page zoom const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel(); // un-zoom the font size to avoid being affected by text-only zoom const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mFont.size);
--- a/content/canvas/test/webgl/failing_tests_linux.txt +++ b/content/canvas/test/webgl/failing_tests_linux.txt @@ -6,9 +6,8 @@ conformance/misc/uninitialized-test.html conformance/programs/gl-get-active-attribute.html conformance/textures/texture-mips.html conformance/uniforms/gl-uniform-bool.html conformance/more/conformance/quickCheckAPI-S_V.html conformance/more/functions/uniformfArrayLen1.html conformance/glsl/misc/attrib-location-length-limits.html conformance/glsl/misc/uniform-location-length-limits.html conformance/renderbuffers/framebuffer-object-attachment.html -conformance/misc/object-deletion-behaviour.html
--- a/content/canvas/test/webgl/failing_tests_mac.txt +++ b/content/canvas/test/webgl/failing_tests_mac.txt @@ -2,11 +2,9 @@ conformance/context/premultiplyalpha-tes conformance/glsl/misc/glsl-function-nodes.html conformance/glsl/misc/glsl-long-variable-names.html conformance/glsl/misc/shader-with-256-character-identifier.frag.html conformance/glsl/misc/shader-with-long-line.html conformance/more/conformance/quickCheckAPI-S_V.html conformance/more/functions/uniformfBadArgs.html conformance/more/functions/uniformiBadArgs.html conformance/glsl/misc/attrib-location-length-limits.html -conformance/glsl/misc/uniform-location-length-limits.html -conformance/renderbuffers/framebuffer-object-attachment.html -conformance/misc/object-deletion-behaviour.html +conformance/glsl/misc/uniform-location-length-limits.html \ No newline at end of file
--- a/content/canvas/test/webgl/failing_tests_windows.txt +++ b/content/canvas/test/webgl/failing_tests_windows.txt @@ -3,11 +3,9 @@ conformance/glsl/functions/glsl-function conformance/glsl/functions/glsl-function-atan-xy.html conformance/glsl/misc/glsl-long-variable-names.html conformance/glsl/misc/shader-with-256-character-identifier.frag.html conformance/glsl/misc/shader-with-long-line.html conformance/more/conformance/quickCheckAPI-S_V.html conformance/more/functions/uniformfArrayLen1.html conformance/glsl/misc/attrib-location-length-limits.html conformance/glsl/misc/struct-nesting-under-maximum.html -conformance/glsl/misc/uniform-location-length-limits.html -conformance/renderbuffers/framebuffer-object-attachment.html -conformance/misc/object-deletion-behaviour.html +conformance/glsl/misc/uniform-location-length-limits.html \ No newline at end of file
--- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -77,16 +77,17 @@ #include "nsDOMJSUtils.h" #include "nsDOMScriptObjectHolder.h" #include "nsDataHashtable.h" #include "nsCOMArray.h" #include "nsEventListenerService.h" #include "nsDOMEvent.h" #include "nsIContentSecurityPolicy.h" #include "nsJSEnvironment.h" +#include "sampler.h" using namespace mozilla::dom; #define EVENT_TYPE_EQUALS( ls, type, userType ) \ (ls->mEventType == type && \ (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType)) static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, @@ -746,16 +747,17 @@ void nsEventListenerManager::HandleEventInternal(nsPresContext* aPresContext, nsEvent* aEvent, nsIDOMEvent** aDOMEvent, nsIDOMEventTarget* aCurrentTarget, PRUint32 aFlags, nsEventStatus* aEventStatus, nsCxPusher* aPusher) { + SAMPLE_LABEL("nsEventListenerManager", "HandleEventInternal"); //Set the value of the internal PreventDefault flag properly based on aEventStatus if (*aEventStatus == nsEventStatus_eConsumeNoDefault) { aEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT; } nsAutoTObserverArray<nsListenerStruct, 2>::EndLimitedIterator iter(mListeners); nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent)); bool hasListener = false;
--- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2511,17 +2511,17 @@ nsEventStateManager::DoScrollZoom(nsIFra nsIContent *content = aTargetFrame->GetContent(); if (content && !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) && !content->IsXUL()) { // positive adjustment to decrease zoom, negative to increase PRInt32 change = (adjustment > 0) ? -1 : 1; - if (Preferences::GetBool("browser.zoom.full")) { + if (Preferences::GetBool("browser.zoom.full") || content->GetCurrentDoc()->IsSyntheticDocument()) { ChangeFullZoom(change); } else { ChangeTextSize(change); } } } static nsIFrame* @@ -2544,17 +2544,18 @@ GetScrollableLineHeight(nsIFrame* aTarge nsIScrollableFrame* sf = f->GetScrollTargetFrame(); if (sf) return sf->GetLineScrollAmount().height; } // Fall back to the font height of the target frame. nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(aTargetFrame, getter_AddRefs(fm), - nsLayoutUtils::FontSizeInflationFor(aTargetFrame)); + nsLayoutUtils::FontSizeInflationFor(aTargetFrame, + nsLayoutUtils::eNotInReflow)); NS_ASSERTION(fm, "FontMetrics is null!"); if (fm) return fm->MaxHeight(); return 0; } void nsEventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame,
--- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -1821,17 +1821,17 @@ nsGenericHTMLElement::MapCommonAttribute else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) { userModify->SetIntValue(NS_STYLE_USER_MODIFY_READ_ONLY, eCSSUnit_Enumerated); } } } } - if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Visibility)) { + if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) { const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::lang); if (value && value->Type() == nsAttrValue::eString) { aData->ValueForLang()->SetStringValue(value->GetStringValue(), eCSSUnit_Ident); } } }
--- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -135,24 +135,37 @@ public: const nsAString& aValue, nsAttrValue& aResult); virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; virtual nsEventStates IntrinsicState() const; virtual nsXPCClassInfo* GetClassInfo(); + + virtual void OnDNSPrefetchDeferred(); + virtual void OnDNSPrefetchRequested(); + virtual bool HasDeferredDNSPrefetchRequest(); }; +// Indicates that a DNS Prefetch has been requested from this Anchor elem +#define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \ + (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) +// Indicates that a DNS Prefetch was added to the deferral queue +#define HTML_ANCHOR_DNS_PREFETCH_DEFERRED \ + (1 << (ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1)) + +// Make sure we have enough space for those bits +PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1 < 32); NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed<nsINodeInfo> aNodeInfo) - : nsGenericHTMLElement(aNodeInfo), - Link(this) + : nsGenericHTMLElement(aNodeInfo) + , Link(this) { } nsHTMLAnchorElement::~nsHTMLAnchorElement() { } @@ -197,16 +210,36 @@ nsHTMLAnchorElement::GetDraggable(bool* nsGkAtoms::_false, eIgnoreCase); return NS_OK; } // no href, so just use the same behavior as other elements return nsGenericHTMLElement::GetDraggable(aDraggable); } +void +nsHTMLAnchorElement::OnDNSPrefetchRequested() +{ + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); + SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); +} + +void +nsHTMLAnchorElement::OnDNSPrefetchDeferred() +{ + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); +} + +bool +nsHTMLAnchorElement::HasDeferredDNSPrefetchRequest() +{ + return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); +} + nsresult nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) { Link::ResetLinkState(false); nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, @@ -223,16 +256,31 @@ nsHTMLAnchorElement::BindToTree(nsIDocum } return rv; } void nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent) { + // Cancel any DNS prefetches + // Note: Must come before ResetLinkState. If called after, it will recreate + // mCachedURI based on data that is invalid - due to a call to GetHostname. + + // If prefetch was deferred, clear flag and move on + if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED)) + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); + // Else if prefetch was requested, clear flag and send cancellation + else if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) { + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + // Possible that hostname could have changed since binding, but since this + // covers common cases, most DNS prefetch requests will be canceled + nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); + } + // If this link is ever reinserted into a document, it might // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false); nsIDocument* doc = GetCurrentDoc(); if (doc) { doc->UnregisterPendingLinkUpdate(this); }
--- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -160,54 +160,112 @@ nsHTMLDNSPrefetch::PrefetchMedium(Link * nsresult nsHTMLDNSPrefetch::PrefetchHigh(Link *aElement) { return Prefetch(aElement, 0); } nsresult -nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) +nsHTMLDNSPrefetch::Prefetch(const nsAString &hostname, PRUint16 flags) { if (IsNeckoChild()) { // We need to check IsEmpty() because net_IsValidHostName() // considers empty strings to be valid hostnames if (!hostname.IsEmpty() && net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) { gNeckoChild->SendHTMLDNSPrefetch(nsAutoString(hostname), flags); } return NS_OK; } if (!(sInitialized && sDNSService && sPrefetches && sDNSListener)) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr<nsICancelable> tmpOutstanding; - return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); + return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), + flags | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, nsnull, + getter_AddRefs(tmpOutstanding)); } nsresult -nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchLow(const nsAString &hostname) { return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW); } nsresult -nsHTMLDNSPrefetch::PrefetchMedium(nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchMedium(const nsAString &hostname) { return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_MEDIUM); } nsresult -nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchHigh(const nsAString &hostname) { return Prefetch(hostname, 0); } +nsresult +nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement, + PRUint16 flags, + nsresult aReason) +{ + if (!(sInitialized && sPrefetches && sDNSService && sDNSListener)) + return NS_ERROR_NOT_AVAILABLE; + + nsAutoString hostname; + nsresult rv = aElement->GetHostname(hostname); + NS_ENSURE_SUCCESS(rv, rv); + return CancelPrefetch(hostname, flags, aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetch(const nsAString &hostname, + PRUint16 flags, + nsresult aReason) +{ + // Forward this request to Necko Parent if we're a child process + if (IsNeckoChild()) { + // We need to check IsEmpty() because net_IsValidHostName() + // considers empty strings to be valid hostnames + if (!hostname.IsEmpty() && + net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) { + gNeckoChild->SendCancelHTMLDNSPrefetch(nsString(hostname), flags, + aReason); + } + return NS_OK; + } + + if (!(sInitialized && sDNSService && sPrefetches && sDNSListener)) + return NS_ERROR_NOT_AVAILABLE; + + // Forward cancellation to DNS service + return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname), + flags + | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason) +{ + return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW, + aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetchLow(const nsAString &hostname, nsresult aReason) +{ + return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW, + aReason); +} + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener, nsIDNSListener) NS_IMETHODIMP nsHTMLDNSPrefetch::nsListener::OnLookupComplete(nsICancelable *request, nsIDNSRecord *rec, @@ -252,16 +310,18 @@ nsHTMLDNSPrefetch::nsDeferrals::Flush() } nsresult nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement) { // The FIFO has no lock, so it can only be accessed on main thread NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Add must be on main thread"); + aElement->OnDNSPrefetchDeferred(); + if (((mHead + 1) & sMaxDeferredMask) == mTail) return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; mEntries[mHead].mFlags = flags; mEntries[mHead].mElement = do_GetWeakReference(aElement); mHead = (mHead + 1) & sMaxDeferredMask; if (!mActiveLoaderCount && !mTimerArmed && mTimer) { @@ -278,30 +338,38 @@ nsHTMLDNSPrefetch::nsDeferrals::SubmitQu NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::SubmitQueue must be on main thread"); nsCString hostName; if (!sDNSService) return; while (mHead != mTail) { nsCOMPtr<nsIContent> content = do_QueryReferent(mEntries[mTail].mElement); if (content) { nsCOMPtr<Link> link = do_QueryInterface(content); - nsCOMPtr<nsIURI> hrefURI(link ? link->GetURI() : nsnull); - if (hrefURI) - hrefURI->GetAsciiHost(hostName); + // Only prefetch here if request was deferred and deferral not cancelled + if (link && link->HasDeferredDNSPrefetchRequest()) { + nsCOMPtr<nsIURI> hrefURI(link ? link->GetURI() : nsnull); + if (hrefURI) + hrefURI->GetAsciiHost(hostName); - if (!hostName.IsEmpty()) { - if (IsNeckoChild()) { - gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), + if (!hostName.IsEmpty()) { + if (IsNeckoChild()) { + gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), mEntries[mTail].mFlags); - } else { - nsCOMPtr<nsICancelable> tmpOutstanding; + } else { + nsCOMPtr<nsICancelable> tmpOutstanding; - sDNSService->AsyncResolve(hostName, - mEntries[mTail].mFlags | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); + nsresult rv = sDNSService->AsyncResolve(hostName, + mEntries[mTail].mFlags + | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, nsnull, + getter_AddRefs(tmpOutstanding)); + // Tell link that deferred prefetch was requested + if (NS_SUCCEEDED(rv)) + link->OnDNSPrefetchRequested(); + } } } } mEntries[mTail].mElement = nsnull; mTail = (mTail + 1) & sMaxDeferredMask; }
--- a/content/html/content/src/nsHTMLDNSPrefetch.h +++ b/content/html/content/src/nsHTMLDNSPrefetch.h @@ -79,23 +79,32 @@ public: // complete, while the string versions submit the lookup to // the DNS system immediately. The URI version is somewhat lighter // weight, but its request is also more likely to be dropped due to a // full queue and it may only be used from the main thread. static nsresult PrefetchHigh(mozilla::dom::Link *aElement); static nsresult PrefetchMedium(mozilla::dom::Link *aElement); static nsresult PrefetchLow(mozilla::dom::Link *aElement); - static nsresult PrefetchHigh(nsAString &host); - static nsresult PrefetchMedium(nsAString &host); - static nsresult PrefetchLow(nsAString &host); + static nsresult PrefetchHigh(const nsAString &host); + static nsresult PrefetchMedium(const nsAString &host); + static nsresult PrefetchLow(const nsAString &host); + static nsresult CancelPrefetchLow(const nsAString &host, nsresult aReason); + static nsresult CancelPrefetchLow(mozilla::dom::Link *aElement, + nsresult aReason); private: - static nsresult Prefetch(nsAString &host, PRUint16 flags); + static nsresult Prefetch(const nsAString &host, PRUint16 flags); static nsresult Prefetch(mozilla::dom::Link *aElement, PRUint16 flags); + static nsresult CancelPrefetch(const nsAString &hostname, + PRUint16 flags, + nsresult aReason); + static nsresult CancelPrefetch(mozilla::dom::Link *aElement, + PRUint16 flags, + nsresult aReason); public: class nsListener : public nsIDNSListener { // This class exists to give a safe callback no-op DNSListener public: NS_DECL_ISUPPORTS NS_DECL_NSIDNSLISTENER
--- a/content/html/content/src/nsTextEditorState.cpp +++ b/content/html/content/src/nsTextEditorState.cpp @@ -212,16 +212,17 @@ public: NS_IMETHOD PageMove(bool aForward, bool aExtend); NS_IMETHOD CompleteScroll(bool aForward); NS_IMETHOD CompleteMove(bool aForward, bool aExtend); NS_IMETHOD ScrollPage(bool aForward); NS_IMETHOD ScrollLine(bool aForward); NS_IMETHOD ScrollCharacter(bool aRight); NS_IMETHOD SelectAll(void); NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, bool *_retval); + virtual nsresult CheckVisibilityContent(nsIContent* aNode, PRInt16 aStartOffset, PRInt16 aEndOffset, bool* aRetval); private: nsRefPtr<nsFrameSelection> mFrameSelection; nsCOMPtr<nsIContent> mLimiter; nsIScrollableFrame *mScrollFrame; nsWeakPtr mPresShellWeak; }; @@ -601,16 +602,32 @@ nsTextInputSelectionImpl::CheckVisibilit if (shell) { return shell->CheckVisibility(node,startOffset,EndOffset, _retval); } return NS_ERROR_FAILURE; } +nsresult +nsTextInputSelectionImpl::CheckVisibilityContent(nsIContent* aNode, + PRInt16 aStartOffset, + PRInt16 aEndOffset, + bool* aRetval) +{ + if (!mPresShellWeak) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak); + NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); + + return shell->CheckVisibilityContent(aNode, aStartOffset, aEndOffset, aRetval); +} + class nsTextInputListener : public nsISelectionListener, public nsIDOMEventListener, public nsIEditorObserver, public nsSupportsWeakReference { public: /** the default constructor */
--- a/content/html/document/src/MediaDocument.cpp +++ b/content/html/document/src/MediaDocument.cpp @@ -44,17 +44,16 @@ #include "nsIScrollable.h" #include "nsIViewManager.h" #include "nsITextToSubURI.h" #include "nsIURL.h" #include "nsIContentViewer.h" #include "nsIMarkupDocumentViewer.h" #include "nsIDocShell.h" #include "nsIParser.h" // kCharsetFrom* macro definition -#include "nsIDocumentCharsetInfo.h" #include "nsNodeInfoManager.h" #include "nsContentUtils.h" namespace mozilla { namespace dom { MediaDocumentStreamListener::MediaDocumentStreamListener(MediaDocument *aDocument) { @@ -180,38 +179,34 @@ MediaDocument::StartDocumentLoad(const c // |UpdateTitleAndCharset| and the worst thing possible is a mangled // filename in the titlebar and the file picker. // When this document is opened in the window/tab of the referring // document (by a simple link-clicking), |prevDocCharacterSet| contains // the charset of the referring document. On the other hand, if the // document is opened in a new window, it is |defaultCharacterSet| of |muCV| // where the charset of our interest is stored. In case of openining - // in a new tab, we get the charset from |documentCharsetInfo|. Note that we + // in a new tab, we get the charset from the docShell. Note that we // exclude UTF-8 as 'invalid' because UTF-8 is likely to be the charset // of a chrome document that has nothing to do with the actual content // whose charset we want to know. Even if "the actual content" is indeed // in UTF-8, we don't lose anything because the default empty value is // considered synonymous with UTF-8. nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer)); // not being able to set the charset is not critical. NS_ENSURE_TRUE(docShell, NS_OK); - nsCOMPtr<nsIDocumentCharsetInfo> dcInfo; nsCAutoString charset; - docShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo)); - if (dcInfo) { - nsCOMPtr<nsIAtom> csAtom; - dcInfo->GetParentCharset(getter_AddRefs(csAtom)); - if (csAtom) { // opening in a new tab - csAtom->ToUTF8String(charset); - } + nsCOMPtr<nsIAtom> csAtom; + docShell->GetParentCharset(getter_AddRefs(csAtom)); + if (csAtom) { // opening in a new tab + csAtom->ToUTF8String(charset); } if (charset.IsEmpty() || charset.Equals("UTF-8")) { nsCOMPtr<nsIContentViewer> cv; docShell->GetContentViewer(getter_AddRefs(cv)); // not being able to set the charset is not critical. NS_ENSURE_TRUE(cv, NS_OK);
--- a/content/html/document/src/PluginDocument.cpp +++ b/content/html/document/src/PluginDocument.cpp @@ -42,16 +42,17 @@ #include "nsIObjectFrame.h" #include "nsNPAPIPluginInstance.h" #include "nsIDocShellTreeItem.h" #include "nsNodeInfoManager.h" #include "nsContentCreatorFunctions.h" #include "nsContentPolicyUtils.h" #include "nsIPropertyBag2.h" #include "mozilla/dom/Element.h" +#include "sampler.h" namespace mozilla { namespace dom { class PluginDocument : public MediaDocument , public nsIPluginDocument { public: @@ -108,16 +109,17 @@ private: nsRefPtr<PluginDocument> mPluginDoc; }; NS_IMETHODIMP PluginStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt) { + SAMPLE_LABEL("PluginStreamListener", "OnStartRequest"); // Have to set up our plugin stuff before we call OnStartRequest, so // that the plugin listener can get that call. nsresult rv = SetupPlugin(); NS_ASSERTION(NS_FAILED(rv) || mNextStream, "We should have a listener by now"); nsresult rv2 = MediaDocumentStreamListener::OnStartRequest(request, ctxt); return NS_SUCCEEDED(rv) ? rv2 : rv;
--- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -68,20 +68,16 @@ #include "mozilla/Preferences.h" #include "nsGenericHTMLElement.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentType.h" #include "nsIScriptElement.h" -#include "nsIDOMHTMLFormElement.h" -#include "nsIFormControl.h" -#include "nsIForm.h" - #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsGkAtoms.h" #include "nsContentUtils.h" #include "nsIChannel.h" #include "nsIHttpChannel.h" #include "nsIDocShell.h" @@ -156,27 +152,24 @@ static const contentCreatorCallback sCon #undef HTML_HTMLELEMENT_TAG #undef HTML_OTHER NS_NewHTMLUnknownElement }; class SinkContext; class HTMLContentSink; -static void MaybeSetForm(nsGenericHTMLElement*, nsHTMLTag, HTMLContentSink*); - class HTMLContentSink : public nsContentSink, #ifdef DEBUG public nsIDebugDumpContent, #endif public nsIHTMLContentSink { public: friend class SinkContext; - friend void MaybeSetForm(nsGenericHTMLElement*, nsHTMLTag, HTMLContentSink*); HTMLContentSink(); virtual ~HTMLContentSink(); NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW nsresult Init(nsIDocument* aDoc, nsIURI* aURI, nsISupports* aContainer, nsIChannel* aChannel); @@ -197,27 +190,23 @@ public: virtual nsISupports *GetTarget(); virtual bool IsScriptExecuting(); // nsIHTMLContentSink NS_IMETHOD OpenContainer(const nsIParserNode& aNode); NS_IMETHOD CloseContainer(const nsHTMLTag aTag); NS_IMETHOD CloseMalformedContainer(const nsHTMLTag aTag); NS_IMETHOD AddLeaf(const nsIParserNode& aNode); - NS_IMETHOD AddComment(const nsIParserNode& aNode); - NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode); - NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode); NS_IMETHOD DidProcessTokens(void); NS_IMETHOD WillProcessAToken(void); NS_IMETHOD DidProcessAToken(void); NS_IMETHOD BeginContext(PRInt32 aID); NS_IMETHOD EndContext(PRInt32 aID); NS_IMETHOD OpenHead(); NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn); - NS_IMETHOD_(bool) IsFormOnStack(); #ifdef DEBUG // nsIDebugDumpContent NS_IMETHOD DumpContentModel(); #endif protected: // If aCheckIfPresent is true, will only set an attribute in cases @@ -238,63 +227,42 @@ protected: nsCOMPtr<nsIHTMLDocument> mHTMLDocument; // The maximum length of a text run PRInt32 mMaxTextRun; nsRefPtr<nsGenericHTMLElement> mRoot; nsRefPtr<nsGenericHTMLElement> mBody; - nsRefPtr<nsGenericHTMLElement> mFrameset; nsRefPtr<nsGenericHTMLElement> mHead; - nsRefPtr<nsGenericHTMLElement> mCurrentForm; - nsAutoTArray<SinkContext*, 8> mContextStack; SinkContext* mCurrentContext; SinkContext* mHeadContext; - PRInt32 mNumOpenIFRAMES; - - // depth of containment within <noembed>, <noframes> etc - PRInt32 mInsideNoXXXTag; // Boolean indicating whether we've seen a <head> tag that might have had // attributes once already. bool mHaveSeenHead; // Boolean indicating whether we've notified insertion of our root content // yet. We want to make sure to only do this once. bool mNotifiedRootInsertion; PRUint8 mScriptEnabled : 1; PRUint8 mFramesEnabled : 1; - PRUint8 mFormOnStack : 1; - PRUint8 unused : 5; // bits available if someone needs one + PRUint8 unused : 6; // bits available if someone needs one nsINodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1]; nsresult FlushTags(); - void StartLayout(bool aIgnorePendingSheets); - // Routines for tags that require special handling nsresult CloseHTML(); - nsresult OpenFrameset(const nsIParserNode& aNode); - nsresult CloseFrameset(); nsresult OpenBody(const nsIParserNode& aNode); nsresult CloseBody(); - nsresult OpenForm(const nsIParserNode& aNode); - nsresult CloseForm(); - nsresult ProcessLINKTag(const nsIParserNode& aNode); - - // Routines for tags that require special handling when we reach their end - // tag. - nsresult ProcessSCRIPTEndTag(nsGenericHTMLElement* content, - bool aMalformed); - nsresult ProcessSTYLEEndTag(nsGenericHTMLElement* content); nsresult OpenHeadContext(); void CloseHeadContext(); // nsContentSink overrides void UpdateChildCounts(); void NotifyInsert(nsIContent* aContent, @@ -313,20 +281,19 @@ class SinkContext { public: SinkContext(HTMLContentSink* aSink); ~SinkContext(); nsresult Begin(nsHTMLTag aNodeType, nsGenericHTMLElement* aRoot, PRUint32 aNumFlushed, PRInt32 aInsertionPoint); nsresult OpenContainer(const nsIParserNode& aNode); - nsresult CloseContainer(const nsHTMLTag aTag, bool aMalformed); + nsresult CloseContainer(const nsHTMLTag aTag); nsresult AddLeaf(const nsIParserNode& aNode); nsresult AddLeaf(nsIContent* aContent); - nsresult AddComment(const nsIParserNode& aNode); nsresult End(); nsresult GrowStack(); nsresult AddText(const nsAString& aText); nsresult FlushText(bool* aDidFlush = nsnull, bool aReleaseLast = false); nsresult FlushTextAndRelease(bool* aDidFlush = nsnull) { @@ -472,49 +439,16 @@ HTMLContentSink::AddAttributes(const nsI // Add attribute to content aContent->SetAttr(kNameSpaceID_None, keyAtom, v, aNotify); } } return NS_OK; } -static void -MaybeSetForm(nsGenericHTMLElement* aContent, nsHTMLTag aNodeType, - HTMLContentSink* aSink) -{ - nsGenericHTMLElement* form = aSink->mCurrentForm; - - if (!form || aSink->mInsideNoXXXTag) { - return; - } - - switch (aNodeType) { - case eHTMLTag_button: - case eHTMLTag_fieldset: - case eHTMLTag_label: - case eHTMLTag_object: - case eHTMLTag_input: - case eHTMLTag_select: - case eHTMLTag_textarea: - break; - default: - return; - } - - nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aContent)); - NS_ASSERTION(formControl, - "nsGenericHTMLElement didn't implement nsIFormControl"); - nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(form)); - NS_ASSERTION(formElement, - "nsGenericHTMLElement didn't implement nsIDOMHTMLFormElement"); - - formControl->SetForm(formElement); -} - /** * Factory subroutine to create all of the html content objects. */ already_AddRefed<nsGenericHTMLElement> HTMLContentSink::CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType) { // Find/create atom for the tag name @@ -655,18 +589,17 @@ SinkContext::IsCurrentContainer(nsHTMLTa } return false; } void SinkContext::DidAddContent(nsIContent* aContent) { - if ((mStackPos == 2) && (mSink->mBody == mStack[1].mContent || - mSink->mFrameset == mStack[1].mContent)) { + if ((mStackPos == 2) && (mSink->mBody == mStack[1].mContent)) { // We just finished adding something to the body mNotifyLevel = 0; } // If we just added content to a node for which // an insertion happen, we need to do an immediate // notification for that insertion. if (0 < mStackPos && @@ -737,73 +670,44 @@ SinkContext::OpenContainer(const nsIPars } mStack[mStackPos].mType = nodeType; mStack[mStackPos].mContent = content; mStack[mStackPos].mNumFlushed = 0; mStack[mStackPos].mInsertionPoint = -1; ++mStackPos; - // XXX Need to do this before we start adding attributes. - if (nodeType == eHTMLTag_style) { - nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(content); - NS_ASSERTION(ssle, "Style content isn't a style sheet?"); - ssle->SetLineNumber(aNode.GetSourceLineNumber()); - - // Now disable updates so that every time we add an attribute or child - // text token, we don't try to update the style sheet. - if (!mSink->mInsideNoXXXTag) { - ssle->InitStyleLinkElement(false); - } - else { - // We're not going to