Merge last green changeset of mozilla-inbound to mozilla-central

This commit is contained in:
Ed Morley 2012-01-06 22:27:52 +00:00
commit d89ef2f8f8
93 changed files with 1211 additions and 373 deletions

2
.gitignore vendored
View File

@ -23,7 +23,7 @@ ID
security/manager/.nss.checkout
# Build directories
obj/*
obj*/
# Build directories for js shell
*/_DBG.OBJ/

View File

@ -32,7 +32,7 @@
<![CDATA[
////////////////////////////////////////////////////////////////////////////
// Hack to make xul:tabbrowser work
// Hacks to make xul:tabbrowser work
const Ci = Components.interfaces;
const CC = Components.classes;
@ -53,6 +53,8 @@
var gFindBarInitialized = false;
function goSetCommandEnabled() {}
////////////////////////////////////////////////////////////////////////////
// Tests

View File

@ -1,3 +1,5 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -60,8 +62,8 @@ XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
// In order to use http:// scheme instead of file:// scheme
// (that is much more restricted) the following code kick-off
// a local http server listening on http://127.0.0.1:8888 and
// http://localhost:8888.
// a local http server listening on http://127.0.0.1:7777 and
// http://localhost:7777.
function startupHttpd(baseDir, port) {
const httpdURL = 'chrome://browser/content/httpd.js';
let httpd = {};
@ -91,6 +93,9 @@ function addPermissions(urls) {
var shell = {
// FIXME/bug 678695: this should be a system setting
preferredScreenBrightness: 1.0,
get home() {
delete this.home;
return this.home = document.getElementById('homescreen');
@ -139,7 +144,7 @@ var shell = {
baseDir.pop();
baseDir = baseDir.join('/');
const SERVER_PORT = 8888;
const SERVER_PORT = 6666;
startupHttpd(baseDir, SERVER_PORT);
let baseHost = 'http://localhost';
@ -195,7 +200,7 @@ var shell = {
this.sendEvent(this.home.contentWindow, 'home');
break;
case evt.DOM_VK_SLEEP:
screen.mozEnabled = !screen.mozEnabled;
this.toggleScreen();
break;
case evt.DOM_VK_ESCAPE:
if (evt.defaultPrevented)
@ -206,6 +211,7 @@ var shell = {
break;
case 'load':
this.home.removeEventListener('load', this, true);
this.turnScreenOn();
this.sendEvent(window, 'ContentStart');
break;
case 'MozApplicationManifest':
@ -248,7 +254,21 @@ var shell = {
let event = content.document.createEvent('CustomEvent');
event.initCustomEvent(type, true, true, details ? details : {});
content.dispatchEvent(event);
}
},
toggleScreen: function shell_toggleScreen() {
if (screen.mozEnabled)
this.turnScreenOff();
else
this.turnScreenOn();
},
turnScreenOff: function shell_turnScreenOff() {
screen.mozEnabled = false;
screen.mozBrightness = 0.0;
},
turnScreenOn: function shell_turnScreenOn() {
screen.mozEnabled = true;
screen.mozBrightness = this.preferredScreenBrightness;
},
};
(function VirtualKeyboardManager() {

View File

@ -40,6 +40,7 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="shell"
width="480" height="800"
windowtype="navigator:browser"
#ifdef ANDROID
sizemode="fullscreen"
#endif

View File

@ -1,12 +1,12 @@
pref("startup.homepage_override_url","http://www.mozilla.org/projects/%APP%/%VERSION%/whatsnew/");
pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERSION%/firstrun/");
// The time interval between checks for a new version (in seconds)
pref("app.update.interval", 7200); // 2 hours
pref("app.update.interval", 3600); // 1 hour
// The time interval between the downloading of mar file chunks in the
// background (in seconds)
pref("app.update.download.backgroundInterval", 60);
// Give the user x seconds to react before showing the big UI. default=12 hours
pref("app.update.promptWaitTime", 43200);
// Give the user x seconds to react before showing the big UI. default=1 hour
pref("app.update.promptWaitTime", 3600);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "http://nightly.mozilla.org/");

View File

@ -42,8 +42,13 @@
* to remove all traces of visiting a site.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
function run_test() {
PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing-wrapper;1";
load("do_test_removeDataFromDomain.js");
do_test();
// Shutdown the download manager.
Services.obs.notifyObservers(null, "quit-application", null);
}

View File

@ -42,8 +42,13 @@
* to remove all traces of visiting a site.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
function run_test() {
PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
load("do_test_removeDataFromDomain_activeDownloads.js");
do_test();
// Shutdown the download manager.
Services.obs.notifyObservers(null, "quit-application", null);
}

View File

@ -42,8 +42,13 @@
* to remove all traces of visiting a site.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
function run_test() {
PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
load("do_test_removeDataFromDomain.js");
do_test();
// Shutdown the download manager.
Services.obs.notifyObservers(null, "quit-application", null);
}

View File

@ -42,8 +42,13 @@
* to remove all traces of visiting a site.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
function run_test() {
PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
load("do_test_removeDataFromDomain_activeDownloads.js");
do_test();
// Shutdown the download manager.
Services.obs.notifyObservers(null, "quit-application", null);
}

View File

@ -37,7 +37,7 @@
}
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#navigator-toolbox > toolbar:not(:-moz-lwtheme),
#browser-bottombox:not(:-moz-lwtheme) {
background-color: @customToolbarColor@;
@ -82,7 +82,7 @@
}
}
@media all and (-moz-windows-compositor) {
@media (-moz-windows-compositor) {
/* These should be hidden w/ glass enabled. Windows draws its own buttons. */
.titlebar-button {
display: none;
@ -348,7 +348,7 @@
}
/* ::::: splitmenu highlight style that imitates Windows 7 start menu ::::: */
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
.splitmenu-menuitem,
.splitmenu-menu {
-moz-appearance: none;

View File

@ -165,14 +165,14 @@
margin: 0 0 2px;
}
@media all and (-moz-windows-classic) {
@media (-moz-windows-classic) {
#appmenu-button {
margin-bottom: 1px;
}
}
%ifndef WINSTRIPE_AERO
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#main-window[sizemode="normal"] #appmenu-button {
margin-bottom: 5px;
}
@ -315,7 +315,7 @@
-moz-border-end: 1px solid ThreeDShadow;
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#appmenu-popup {
-moz-appearance: none;
background: white;
@ -483,7 +483,7 @@
-moz-appearance: -moz-window-titlebar-maximized;
}
@media all and (-moz-windows-classic) {
@media (-moz-windows-classic) {
#main-window[sizemode="normal"] > #titlebar > #titlebar-content > #appmenu-button-container {
margin-top: 4px;
}
@ -1214,7 +1214,7 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
-moz-padding-end: 2px;
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#urlbar,
.searchbar-textbox {
@navbarTextboxCustomBorder@
@ -1712,7 +1712,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
margin: 16px 0 -2px;
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
.panel-promo-box {
margin: 8px -16px -16px;
padding: 8px 16px;
@ -1795,7 +1795,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
}
%ifndef WINSTRIPE_AERO
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#main-window[sizemode=normal] #TabsToolbar {
padding-left: 2px;
padding-right: 2px;
@ -1827,7 +1827,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
}
%ifndef WINSTRIPE_AERO
@media all and (-moz-windows-theme: luna-blue) {
@media (-moz-windows-theme: luna-blue) {
.tabbrowser-tab,
.tabs-newtab-button {
background-image: @toolbarShadowOnTab@,
@ -1956,7 +1956,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* Tab scrollbox arrow, tabstrip new tab and all-tabs buttons */
@media all and (-moz-touch-enabled) {
@media (-moz-touch-enabled) {
.tabbrowser-arrowscrollbox > .scrollbutton-up,
.tabbrowser-arrowscrollbox > .scrollbutton-down,
#TabsToolbar .toolbarbutton-1 {

View File

@ -21,7 +21,7 @@
}
}
@media all and (-moz-windows-compositor) {
@media (-moz-windows-compositor) {
#placesToolbox {
border-top: none;
}
@ -31,7 +31,7 @@
}
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#placesView,
#searchModifiers,
#infoPane,

View File

@ -44,13 +44,22 @@ public interface Actions {
public enum SpecialKey {
DOWN, UP, LEFT, RIGHT, ENTER
}
public interface EventExpecter {
/** Blocks until the event has been received. Subsequent calls will return immediately. */
public void blockForEvent();
/** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
public boolean eventReceived();
}
/**
* Waits for a gecko event to be sent from the Gecko instance.
* Listens for a gecko event to be sent from the Gecko instance.
* The returned object can be used to test if the event has been
* received. Note that only one event is listened for.
*
* @param geckoEvent The geckoEvent JSONObject's type
*/
void waitForGeckoEvent(String geckoEvent);
EventExpecter expectGeckoEvent(String geckoEvent);
// Send the string kewsToSend to the application
void sendKeys(String keysToSend);
//Send any of the above keys to the element

View File

@ -38,17 +38,20 @@
* ***** END LICENSE BLOCK ***** */
package @ANDROID_PACKAGE_NAME@;
import java.util.List;
import android.app.Activity;
public interface Driver {
/**
* Find the first Element using the given method.
*
* @param activity The activity the element belongs to
* @param name The name of the element
* @return The first matching element on the current context
* @throws RoboCopException If no matching elements are found
*/
Element findElement(String name);
Element findElement(Activity activity, String name);
/**
* Sets up scroll handling so that data is received from the extension.

View File

@ -84,9 +84,6 @@ public class FennecNativeActions implements Actions {
private Method sendGE;
// If waiting for an event.
private SynchronousQueue waitqueue = new SynchronousQueue<Boolean>();
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
this.solo = robocop;
this.instr = instrumentation;
@ -116,7 +113,12 @@ public class FennecNativeActions implements Actions {
}
class wakeInvocationHandler implements InvocationHandler {
public wakeInvocationHandler(){};
private final GeckoEventExpecter mEventExpecter;
public wakeInvocationHandler(GeckoEventExpecter expecter) {
mEventExpecter = expecter;
}
public Object invoke(Object proxy, Method method, Object[] args) {
String methodName = method.getName();
//Depending on the method, return a completely different type.
@ -133,12 +135,54 @@ public class FennecNativeActions implements Actions {
return 314;
}
Log.i("Robocop", "Waking up on "+methodName);
waitqueue.offer(new Boolean(true));
mEventExpecter.notifyOfEvent();
return null;
}
}
class GeckoEventExpecter implements EventExpecter {
private final String mGeckoEvent;
private final Object[] mRegistrationParams;
private boolean mEventReceived;
GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
mGeckoEvent = geckoEvent;
mRegistrationParams = registrationParams;
}
public synchronized void blockForEvent() {
while (! mEventReceived) {
try {
this.wait();
} catch (InterruptedException ie) {
ie.printStackTrace();
break;
}
}
Log.i("Robocop", "unblocked on expecter for " + mGeckoEvent);
}
public synchronized boolean eventReceived() {
return mEventReceived;
}
void notifyOfEvent() {
try {
unregisterGEL.invoke(null, mRegistrationParams);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("Robocop", "received event " + mGeckoEvent);
synchronized (this) {
mEventReceived = true;
this.notifyAll();
}
}
}
public void waitForGeckoEvent(String geckoEvent) {
public EventExpecter expectGeckoEvent(String geckoEvent) {
Log.i("Robocop", "waiting for "+geckoEvent);
try {
Class [] interfaces = new Class[1];
@ -146,21 +190,19 @@ public class FennecNativeActions implements Actions {
Object[] finalParams = new Object[2];
finalParams[0] = geckoEvent;
wakeInvocationHandler wIH = new wakeInvocationHandler();
GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH);
finalParams[1] = proxy;
registerGEL.invoke(null, finalParams);
waitqueue.take();
unregisterGEL.invoke(null, finalParams);
return expecter;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("Robocop", "wait ends for: "+geckoEvent);
return null;
}
public void sendSpecialKey(SpecialKey button) {

View File

@ -167,8 +167,7 @@ public class FennecNativeDriver implements Driver {
return geckoWidth;
}
@Override
public Element findElement(String name) {
public Element findElement(Activity activity, String name) {
if (name == null)
throw new IllegalArgumentException("Can not findElements when passed a null");
if (locators.containsKey(name)){

View File

@ -56,28 +56,26 @@ import com.jayway.android.robotium.solo.Solo;
import java.util.concurrent.SynchronousQueue;
public class FennecNativeElement implements Element {
private final Activity mActivity;
private Integer id;
private Activity currentActivity;
private Solo robocop;
public FennecNativeElement(Integer id, Activity activity, Solo solo){
this.id = id;
mActivity = activity;
robocop = solo;
currentActivity = activity;
}
public Integer getId() {
return id;
}
@Override
public void click() {
final SynchronousQueue syncQueue = new SynchronousQueue();
currentActivity = robocop.getCurrentActivity();
currentActivity.runOnUiThread(
mActivity.runOnUiThread(
new Runnable() {
public void run() {
View view = (View)currentActivity.findViewById(id);
View view = (View)mActivity.findViewById(id);
if(view != null) {
view.performClick();
} else {
@ -94,16 +92,13 @@ public class FennecNativeElement implements Element {
}
private Object text;
private Activity elementActivity;
@Override
public String getText() {
elementActivity = robocop.getCurrentActivity();
final SynchronousQueue syncQueue = new SynchronousQueue();
elementActivity.runOnUiThread(
mActivity.runOnUiThread(
new Runnable() {
public void run() {
View v = elementActivity.findViewById(id);
View v = mActivity.findViewById(id);
if(v instanceof EditText) {
EditText et = (EditText)v;
text = et.getEditableText();
@ -143,15 +138,13 @@ public class FennecNativeElement implements Element {
private boolean displayed;
@Override
public boolean isDisplayed() {
final SynchronousQueue syncQueue = new SynchronousQueue();
currentActivity = robocop.getCurrentActivity();
displayed = false;
currentActivity.runOnUiThread(
mActivity.runOnUiThread(
new Runnable() {
public void run() {
View view = (View)currentActivity.findViewById(id);
View view = (View)mActivity.findViewById(id);
if(view != null) {
displayed = true;
}

View File

@ -8907,16 +8907,9 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
// Remember this is the requesting full-screen document.
sFullScreenDoc = do_GetWeakReference(static_cast<nsIDocument*>(this));
// Make the window full-screen. Note we must make the state changes above
// before making the window full-screen, as then the document reports as
// being in full-screen mode when the chrome "fullscreen" event fires,
// enabling chrome to distinguish between browser and dom full-screen
// modes. Also note that nsGlobalWindow::SetFullScreen() (which
// SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
// and does not operate on the a per-nsIDOMWindow basis.
SetWindowFullScreen(this, true);
#ifdef DEBUG
// Note assertions must run before SetWindowFullScreen() as that does
// synchronous event dispatch which can run script which exits full-screen!
NS_ASSERTION(GetFullScreenElement() == aElement,
"Full-screen element should be the requested element!");
NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
@ -8926,6 +8919,15 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
NS_ASSERTION(c->AsElement() == aElement,
"GetMozFullScreenElement should match GetFullScreenElement()");
#endif
// Make the window full-screen. Note we must make the state changes above
// before making the window full-screen, as then the document reports as
// being in full-screen mode when the chrome "fullscreen" event fires,
// enabling chrome to distinguish between browser and dom full-screen
// modes. Also note that nsGlobalWindow::SetFullScreen() (which
// SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
// and does not operate on the a per-nsIDOMWindow basis.
SetWindowFullScreen(this, true);
}
NS_IMETHODIMP

View File

@ -1568,6 +1568,7 @@ nsWebSocket::Cancel(nsresult aStatus)
return NS_OK;
ConsoleError();
mClientReasonCode = nsIWebSocketChannel::CLOSE_GOING_AWAY;
return CloseConnection();
}

View File

@ -302,6 +302,11 @@ NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest*
{
nsContentUtils::UnregisterShutdownObserver(this);
if (!mElement) {
// We've been notified by the shutdown observer, and are shutting down.
return NS_BINDING_ABORTED;
}
// The element is only needed until we've had a chance to call
// InitializeDecoderForChannel. So make sure mElement is cleared here.
nsRefPtr<nsHTMLMediaElement> element;

View File

@ -273,7 +273,7 @@ nsresult nsBuiltinDecoder::Play()
* Returns true if aValue is inside a range of aRanges, and put the range
* index in aIntervalIndex if it is not null.
* If aValue is not inside a range, false is returned, and aIntervalIndex, if
* not null, is set to the index of the range which ends immediatly before aValue
* not null, is set to the index of the range which ends immediately before aValue
* (and can be -1 if aValue is before aRanges.Start(0)).
*/
static bool IsInRanges(nsTimeRanges& aRanges, double aValue, PRInt32& aIntervalIndex) {
@ -315,30 +315,32 @@ nsresult nsBuiltinDecoder::Seek(double aTime)
}
// If the position we want to seek to is not in a seekable range, we seek
// to the closest position in the seekable ranges instead . If two positions
// are equaly close, we seek to the closest position from the currentTime.
// to the closest position in the seekable ranges instead. If two positions
// are equally close, we seek to the closest position from the currentTime.
// See seeking spec, point 7 :
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#seeking
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#seeking
PRInt32 range = 0;
if (!IsInRanges(seekable, aTime, range)) {
if (range != -1) {
double leftBound, rightBound;
res = seekable.End(range, &leftBound);
NS_ENSURE_SUCCESS(res, NS_OK);
double distanceLeft = NS_ABS(leftBound - aTime);
double distanceRight = -1;
if (range + 1 < length) {
res = seekable.Start(range+1, &rightBound);
double leftBound, rightBound;
res = seekable.End(range, &leftBound);
NS_ENSURE_SUCCESS(res, NS_OK);
res = seekable.Start(range + 1, &rightBound);
NS_ENSURE_SUCCESS(res, NS_OK);
double distanceLeft = NS_ABS(leftBound - aTime);
double distanceRight = NS_ABS(rightBound - aTime);
if (distanceLeft == distanceRight) {
distanceLeft = NS_ABS(leftBound - mCurrentTime);
distanceRight = NS_ABS(rightBound - mCurrentTime);
}
aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
} else {
// Seek target is after the end last range in seekable data.
// Clamp the seek target to the end of the last seekable range.
res = seekable.End(length - 1, &aTime);
NS_ENSURE_SUCCESS(res, NS_OK);
distanceRight = NS_ABS(rightBound - aTime);
}
if (distanceLeft == distanceRight) {
distanceLeft = NS_ABS(leftBound - mCurrentTime);
distanceRight = NS_ABS(rightBound - mCurrentTime);
}
aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
} else {
// aTime is before the first range in |seekable|, the closest point we can
// seek to is the start of the first range.

View File

@ -1124,6 +1124,33 @@ void nsBuiltinDecoderStateMachine::ResetPlayback()
mAudioCompleted = false;
}
void nsBuiltinDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
PRUint32 aLength,
PRUint32 aOffset)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
// While playing an unseekable stream of unknown duration, mEndTime is
// updated (in AdvanceFrame()) as we play. But if data is being downloaded
// faster than played, mEndTime won't reflect the end of playable data
// since we haven't played the frame at the end of buffered data. So update
// mEndTime here as new data is downloaded to prevent such a lag.
nsTimeRanges buffered;
if (mDecoder->IsInfinite() &&
NS_SUCCEEDED(mDecoder->GetBuffered(&buffered)))
{
PRUint32 length = 0;
buffered.GetLength(&length);
if (length) {
double end = 0;
buffered.End(length - 1, &end);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mEndTime = NS_MAX<PRInt64>(mEndTime, end * USECS_PER_S);
}
}
}
void nsBuiltinDecoderStateMachine::Seek(double aTime)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -1505,7 +1532,11 @@ void nsBuiltinDecoderStateMachine::DecodeSeek()
// if we need to seek again.
nsCOMPtr<nsIRunnable> stopEvent;
if (GetMediaTime() == mEndTime) {
bool isLiveStream = mDecoder->GetStream()->GetLength() == -1;
if (GetMediaTime() == mEndTime && !isLiveStream) {
// Seeked to end of media, move to COMPLETED state. Note we don't do
// this if we're playing a live stream, since the end of media will advance
// once we download more data!
LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
mDecoder.get(), seekTime));
stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStoppedAtEnd);

View File

@ -223,10 +223,7 @@ public:
return 0;
}
void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
}
void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset);
PRInt64 GetEndMediaTime() const {
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();

View File

@ -1619,6 +1619,55 @@ nsDOMWindowUtils::GetLayerManagerType(nsAString& aType)
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::StartFrameTimeRecording()
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return NS_ERROR_FAILURE;
LayerManager *mgr = widget->GetLayerManager();
if (!mgr)
return NS_ERROR_FAILURE;
mgr->StartFrameTimeRecording();
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::StopFrameTimeRecording(PRUint32 *frameCount NS_OUTPARAM, float **frames NS_OUTPARAM)
{
NS_ENSURE_ARG_POINTER(frameCount);
NS_ENSURE_ARG_POINTER(frames);
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return NS_ERROR_FAILURE;
LayerManager *mgr = widget->GetLayerManager();
if (!mgr)
return NS_ERROR_FAILURE;
nsTArray<float> frameTimes = mgr->StopFrameTimeRecording();
*frames = nsnull;
*frameCount = frameTimes.Length();
if (*frameCount != 0) {
*frames = (float*)nsMemory::Alloc(*frameCount * sizeof(float*));
if (!*frames)
return NS_ERROR_OUT_OF_MEMORY;
/* copy over the frame times into the array we just allocated */
for (PRUint32 i = 0; i < *frameCount; i++) {
(*frames)[i] = frameTimes[i];
}
}
return NS_OK;
}
static bool
ComputeAnimationValue(nsCSSProperty aProperty,
Element* aElement,

View File

@ -252,13 +252,6 @@ IndexedDatabaseManager::GetOrCreate()
false);
NS_ENSURE_SUCCESS(rv, nsnull);
// We don't really need this callback but we want the observer service to
// hold us alive until XPCOM shutdown. That way other consumers can continue
// to use this service until shutdown.
rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
false);
NS_ENSURE_SUCCESS(rv, nsnull);
// Make a lazy thread for any IO we need (like clearing or enumerating the
// contents of indexedDB database directories).
instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
@ -1224,7 +1217,7 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
// Setting this flag prevents the service from being recreated and prevents
// further databases from being created.
if (PR_ATOMIC_SET(&gShutdown, 1)) {
@ -1279,11 +1272,6 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
return NS_OK;
}
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
// We're dying now.
return NS_OK;
}
NS_NOTREACHED("Unknown topic!");
return NS_ERROR_UNEXPECTED;
}

View File

@ -69,7 +69,7 @@ interface nsIDOMBlob;
interface nsIDOMFile;
interface nsIFile;
[scriptable, uuid(c1fa9c82-acf2-4b27-8ca7-7d1864e606af)]
[scriptable, uuid(15fcceb0-37ea-11e1-b86c-0800200c9a66)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -838,6 +838,9 @@ interface nsIDOMWindowUtils : nsISupports {
*/
readonly attribute AString layerManagerType;
void startFrameTimeRecording();
void stopFrameTimeRecording([optional] out unsigned long frameCount,
[retval, array, size_is(frameCount)] out float frameTime);
/**
* The DPI of the display
*/

View File

@ -208,7 +208,7 @@ nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA temp_store = MEMORY"));
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY"));
NS_ENSURE_SUCCESS(rv, rv);
mozStorageTransaction transaction(mConnection, false);

View File

@ -561,6 +561,31 @@ PlanarYCbCrImage::CopyData(Data& aDest, gfxIntSize& aDestSize,
return buffer;
}
void
LayerManager::StartFrameTimeRecording()
{
mLastFrameTime = TimeStamp::Now();
}
void
LayerManager::PostPresent()
{
if (!mLastFrameTime.IsNull()) {
TimeStamp now = TimeStamp::Now();
mFrameTimes.AppendElement((now - mLastFrameTime).ToMilliseconds());
mLastFrameTime = now;
}
}
nsTArray<float>
LayerManager::StopFrameTimeRecording()
{
mLastFrameTime = TimeStamp();
nsTArray<float> result = mFrameTimes;
mFrameTimes.Clear();
return result;
}
#ifdef MOZ_LAYERS_HAVE_LOG

View File

@ -51,6 +51,7 @@
#include "nsTArray.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/TimeStamp.h"
#if defined(DEBUG) || defined(PR_LOGGING)
# include <stdio.h> // FILE
@ -512,6 +513,11 @@ public:
*/
void LogSelf(const char* aPrefix="");
void StartFrameTimeRecording();
nsTArray<float> StopFrameTimeRecording();
void PostPresent();
static bool IsLogEnabled();
static PRLogModuleInfo* GetLog() { return sLog; }
@ -532,6 +538,9 @@ protected:
static void InitLog();
static PRLogModuleInfo* sLog;
private:
TimeStamp mLastFrameTime;
nsTArray<float> mFrameTimes;
};
class ThebesLayer;

View File

@ -778,6 +778,7 @@ LayerManagerD3D10::Render()
} else {
mSwapChain->Present(0, 0);
}
LayerManager::PostPresent();
}
void

View File

@ -354,6 +354,7 @@ LayerManagerD3D9::Render()
(r = iter.Next()) != nsnull;) {
mSwapChain->Present(*r);
}
LayerManager::PostPresent();
} else {
PaintToTarget();
}

View File

@ -819,6 +819,7 @@ LayerManagerOGL::Render()
if (mGLContext->IsDoubleBuffered()) {
mGLContext->SwapBuffers();
LayerManager::PostPresent();
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
return;
}

View File

@ -774,7 +774,7 @@ struct ParamTraits<LogData> {
int type;
bool result =
ReadParam(m, iter, &r->channel) &&
ReadParam(m, iter, &r->routing_id);
ReadParam(m, iter, &r->routing_id) &&
ReadParam(m, iter, &type) &&
ReadParam(m, iter, &r->flags) &&
ReadParam(m, iter, &r->sent) &&

View File

@ -0,0 +1,8 @@
function foo(x) {
x.a = 10;
assertEq(x.a, 0);
}
x = {a:0,b:1};
Object.freeze(x);
foo(x);

View File

@ -1912,7 +1912,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
for (size_t i = 0, len = props.length(); i < len; i++) {
jsid id = props[i];
if (JSID_IS_STRING(id)) {
JS_ALWAYS_TRUE(vals.append(StringValue(JSID_TO_STRING(id))));
vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
} else if (JSID_IS_INT(id)) {
JSString *str = js_IntToString(cx, JSID_TO_INT(id));
if (!str)
@ -1924,7 +1924,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
}
JS_ASSERT(props.length() <= UINT32_MAX);
JSObject *aobj = NewDenseCopiedArray(cx, jsuint(vals.length()), vals.begin());
JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
if (!aobj)
return false;
vp->setObject(*aobj);
@ -2698,6 +2698,15 @@ obj_preventExtensions(JSContext *cx, uintN argc, Value *vp)
return obj->preventExtensions(cx, &props);
}
/* static */ inline uintN
JSObject::getSealedOrFrozenAttributes(uintN attrs, ImmutabilityType it)
{
/* Make all attributes permanent; if freezing, make data attributes read-only. */
if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
return JSPROP_PERMANENT | JSPROP_READONLY;
return JSPROP_PERMANENT;
}
bool
JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
{
@ -2718,27 +2727,62 @@ JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
/* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
JS_ASSERT(!self->isDenseArray());
for (size_t i = 0, len = props.length(); i < len; i++) {
jsid id = props[i];
uintN attrs;
if (!self->getGenericAttributes(cx, id, &attrs))
if (isNative() && !inDictionaryMode()) {
/*
* Seal/freeze non-dictionary objects by constructing a new shape
* hierarchy mirroring the original one, which can be shared if many
* objects with the same structure are sealed/frozen. If we use the
* generic path below then any non-empty object will be converted to
* dictionary mode.
*/
Shape *last = EmptyShape::getInitialShape(cx, self->getClass(),
self->getProto(),
self->getParent(),
self->getAllocKind(),
self->lastProperty()->getObjectFlags());
if (!last)
return false;
/* Make all attributes permanent; if freezing, make data attributes read-only. */
uintN new_attrs;
if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
new_attrs = JSPROP_PERMANENT | JSPROP_READONLY;
else
new_attrs = JSPROP_PERMANENT;
/* Get an in order list of the shapes in this object. */
AutoShapeVector shapes(cx);
for (Shape::Range r = self->lastProperty()->all(); !r.empty(); r.popFront()) {
if (!shapes.append(&r.front()))
return false;
}
Reverse(shapes.begin(), shapes.end());
/* If we already have the attributes we need, skip the setAttributes call. */
if ((attrs | new_attrs) == attrs)
continue;
for (size_t i = 0; i < shapes.length(); i++) {
StackShape child(shapes[i]);
child.attrs |= getSealedOrFrozenAttributes(child.attrs, it);
attrs |= new_attrs;
if (!self->setGenericAttributes(cx, id, &attrs))
return false;
if (!JSID_IS_EMPTY(child.propid))
MarkTypePropertyConfigured(cx, self, child.propid);
last = JS_PROPERTY_TREE(cx).getChild(cx, last, self->numFixedSlots(), child);
if (!last)
return NULL;
}
JS_ASSERT(self->lastProperty()->slotSpan() == last->slotSpan());
JS_ALWAYS_TRUE(setLastProperty(cx, last));
} else {
for (size_t i = 0; i < props.length(); i++) {
jsid id = props[i];
uintN attrs;
if (!self->getGenericAttributes(cx, id, &attrs))
return false;
uintN new_attrs = getSealedOrFrozenAttributes(attrs, it);
/* If we already have the attributes we need, skip the setAttributes call. */
if ((attrs | new_attrs) == attrs)
continue;
attrs |= new_attrs;
if (!self->setGenericAttributes(cx, id, &attrs))
return false;
}
}
return true;

View File

@ -968,6 +968,8 @@ struct JSObject : js::gc::Cell
bool isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp);
static inline uintN getSealedOrFrozenAttributes(uintN attrs, ImmutabilityType it);
inline void *&privateRef(uint32_t nfixed) const;
public:

View File

@ -300,7 +300,6 @@ JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp
{
JS_ASSERT(nativeContains(cx, shape));
JS_ASSERT(shape.isMethod());
JS_ASSERT(shape.writable());
JS_ASSERT(shape.hasSlot());
JS_ASSERT(shape.hasDefaultSetter());
JS_ASSERT(!isGlobal()); /* i.e. we are not changing the global shape */

View File

@ -1071,7 +1071,7 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n
RootObject selfRoot(cx, &self);
RootShape newRoot(cx, &newShape);
if (!toDictionaryMode(cx))
return false;
return NULL;
oldShape = lastProperty();
}
@ -1080,7 +1080,7 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n
RootShape oldRoot(cx, &oldShape);
newShape = js_NewGCShape(cx);
if (!newShape)
return false;
return NULL;
new (newShape) Shape(oldShape->base()->unowned(), 0);
}

View File

@ -1916,7 +1916,7 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
dp++;
}
}
JS_ALWAYS_TRUE(rdata.sb.append(cp, repstr->length() - (cp - bp)));
rdata.sb.infallibleAppend(cp, repstr->length() - (cp - bp));
}
static bool

View File

@ -882,8 +882,11 @@ FrameState::discardForJoin(RegisterAllocation *&alloc, uint32_t stackDepth)
}
regstate(reg).associate(fe, RematInfo::DATA);
if (!alloc->synced(reg))
if (!alloc->synced(reg)) {
fe->data.unsync();
if (!reg.isReg())
fe->type.unsync();
}
}
a->sp = a->spBase + stackDepth;

View File

@ -5064,6 +5064,9 @@ ProcessArgs(JSContext *cx, JSObject *obj, OptionParser *op)
if (op->getBoolOption('a'))
JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
if (op->getBoolOption('c'))
compileOnly = true;
if (op->getBoolOption('m')) {
enableMethodJit = true;
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
@ -5331,6 +5334,7 @@ main(int argc, char **argv, char **envp)
|| !op.addBoolOption('i', "shell", "Enter prompt after running code")
|| !op.addBoolOption('m', "methodjit", "Enable the JaegerMonkey method JIT")
|| !op.addBoolOption('n', "typeinfer", "Enable type inference")
|| !op.addBoolOption('c', "compileonly", "Only compile, don't run (syntax checking mode)")
|| !op.addBoolOption('d', "debugjit", "Enable runtime debug mode for method JIT code")
|| !op.addBoolOption('a', "always-mjit",
"Do not try to run in the interpreter before method jitting.")

View File

@ -437,6 +437,7 @@ abstract public class GeckoApp
MenuItem share = aMenu.findItem(R.id.share);
MenuItem agentMode = aMenu.findItem(R.id.agent_mode);
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
MenuItem downloads = aMenu.findItem(R.id.downloads);
if (tab == null) {
bookmark.setEnabled(false);
@ -453,11 +454,9 @@ abstract public class GeckoApp
if (tab.isBookmark()) {
bookmark.setChecked(true);
bookmark.setIcon(R.drawable.ic_menu_bookmark_remove);
bookmark.setTitle(R.string.bookmark_remove);
} else {
bookmark.setChecked(false);
bookmark.setIcon(R.drawable.ic_menu_bookmark_add);
bookmark.setTitle(R.string.bookmark_add);
}
forward.setEnabled(tab.canDoForward());
@ -473,6 +472,10 @@ abstract public class GeckoApp
saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
// DownloadManager support is tied to level 12 and higher
if (Build.VERSION.SDK_INT < 12)
downloads.setVisible(false);
return true;
}
@ -498,12 +501,10 @@ abstract public class GeckoApp
tab.removeBookmark();
Toast.makeText(this, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
item.setIcon(R.drawable.ic_menu_bookmark_add);
item.setTitle(R.string.bookmark_add);
} else {
tab.addBookmark();
Toast.makeText(this, R.string.bookmark_added, Toast.LENGTH_SHORT).show();
item.setIcon(R.drawable.ic_menu_bookmark_remove);
item.setTitle(R.string.bookmark_remove);
}
}
return true;
@ -533,6 +534,10 @@ abstract public class GeckoApp
case R.id.addons:
loadUrlInTab("about:addons");
return true;
case R.id.downloads:
intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
startActivity(intent);
return true;
case R.id.agent_mode:
Tab selectedTab = Tabs.getInstance().getSelectedTab();
if (selectedTab == null)
@ -975,6 +980,13 @@ abstract public class GeckoApp
String host = message.getString("host");
JSONArray permissions = message.getJSONArray("permissions");
showSiteSettingsDialog(host, permissions);
} else if (event.equals("Downloads:Done")) {
String displayName = message.getString("displayName");
String path = message.getString("path");
String mimeType = message.getString("mimeType");
int size = message.getInt("size");
handleDownloadDone(displayName, path, mimeType, size);
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
@ -1302,6 +1314,17 @@ abstract public class GeckoApp
tabs.closeTab(tab);
}
void handleDownloadDone(String displayName, String path, String mimeType, int size) {
// DownloadManager.addCompletedDownload is supported in level 12 and higher
if (Build.VERSION.SDK_INT >= 12) {
DownloadManager dm = (DownloadManager) mAppContext.getSystemService(Context.DOWNLOAD_SERVICE);
dm.addCompletedDownload(displayName, displayName,
false /* do not use media scanner */,
mimeType, path, size,
false /* no notification */);
}
}
void addPluginView(final View view,
final double x, final double y,
final double w, final double h) {
@ -1559,6 +1582,7 @@ abstract public class GeckoApp
GeckoAppShell.registerGeckoEventListener("AgentMode:Changed", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
mConnectivityFilter = new IntentFilter();
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
@ -1794,6 +1818,7 @@ abstract public class GeckoApp
GeckoAppShell.unregisterGeckoEventListener("AgentMode:Changed", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
mFavicons.close();

View File

@ -37,6 +37,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.ViewportMetrics;
import android.os.*;
import android.app.*;
@ -78,6 +79,7 @@ public class GeckoEvent {
public static final int ACTIVITY_START = 17;
public static final int BROADCAST = 19;
public static final int VIEWPORT = 20;
public static final int TILE_SIZE = 21;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
@ -228,6 +230,16 @@ public class GeckoEvent {
mP1 = new Point(screenw, screenh);
}
public GeckoEvent(int etype, IntSize size) {
if (etype != TILE_SIZE) {
mType = INVALID;
return;
}
mType = etype;
mP0 = new Point(size.width, size.height);
}
public GeckoEvent(String subject, String data) {
mType = BROADCAST;
mCharacters = subject;

View File

@ -107,6 +107,7 @@ JAVAFILES = \
gfx/LayerController.java \
gfx/LayerRenderer.java \
gfx/LayerView.java \
gfx/MultiTileLayer.java \
gfx/NinePatchTileLayer.java \
gfx/PanningPerfAPI.java \
gfx/PlaceholderLayerClient.java \

View File

@ -43,8 +43,8 @@ import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerRenderer;
import org.mozilla.gecko.gfx.MultiTileLayer;
import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.gfx.WidgetTileLayer;
import org.mozilla.gecko.FloatUtils;
import org.mozilla.gecko.GeckoApp;
@ -53,6 +53,7 @@ import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@ -84,6 +85,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
private CairoImage mCairoImage;
private static final IntSize TILE_SIZE = new IntSize(256, 256);
private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L;
private long mLastViewportChangeTime;
private boolean mPendingViewportAdjust;
@ -112,7 +115,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
public int getFormat() { return mFormat; }
};
mTileLayer = new SingleTileLayer(mCairoImage);
mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE);
}
@ -143,6 +146,14 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
// This needs to happen before a call to sendResizeEventIfNecessary
// happens, but only needs to be called once. As that is only called by
// the layer controller or this, here is a safe place to do so.
if (mTileLayer instanceof MultiTileLayer) {
GeckoEvent event = new GeckoEvent(GeckoEvent.TILE_SIZE, TILE_SIZE);
GeckoAppShell.sendEventToGecko(event);
}
sendResizeEventIfNecessary();
}
@ -213,8 +224,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
mUpdateViewportOnEndDraw = false;
Rect rect = new Rect(x, y, x + width, y + height);
if (mTileLayer instanceof SingleTileLayer)
((SingleTileLayer)mTileLayer).invalidate(rect);
if (mTileLayer instanceof MultiTileLayer)
((MultiTileLayer)mTileLayer).invalidate(rect);
} finally {
endTransaction(mTileLayer);
}
@ -228,6 +239,33 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
return null;
}
public void copyPixelsFromMultiTileLayer(Bitmap target) {
Canvas c = new Canvas(target);
ByteBuffer tileBuffer = mBuffer.slice();
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
for (int y = 0; y < mBufferSize.height; y += TILE_SIZE.height) {
for (int x = 0; x < mBufferSize.width; x += TILE_SIZE.width) {
// Calculate tile size
IntSize tileSize = new IntSize(Math.min(mBufferSize.width - x, TILE_SIZE.width),
Math.min(mBufferSize.height - y, TILE_SIZE.height));
// Create a Bitmap from this tile
Bitmap tile = Bitmap.createBitmap(tileSize.width, tileSize.height,
CairoUtils.cairoFormatTobitmapConfig(mFormat));
tile.copyPixelsFromBuffer(tileBuffer.asIntBuffer());
// Copy the tile to the master Bitmap and recycle it
c.drawBitmap(tile, x, y, null);
tile.recycle();
// Progress the buffer to the next tile
tileBuffer.position(tileSize.getArea() * bpp);
tileBuffer = tileBuffer.slice();
}
}
}
public Bitmap getBitmap() {
// Begin a tile transaction, otherwise the buffer can be destroyed while
// we're reading from it.
@ -238,7 +276,12 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
try {
Bitmap b = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height,
CairoUtils.cairoFormatTobitmapConfig(mFormat));
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
if (mTileLayer instanceof MultiTileLayer)
copyPixelsFromMultiTileLayer(b);
else
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
return b;
} catch (OutOfMemoryError oom) {
Log.w(LOGTAG, "Unable to create bitmap", oom);
@ -280,15 +323,25 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
}
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
int maxSize = getLayerController().getView().getMaxTextureSize();
IntSize bufferSize;
// XXX Introduce tiling to solve this?
if (mScreenSize.width > maxSize || mScreenSize.height > maxSize)
throw new RuntimeException("Screen size of " + mScreenSize + " larger than maximum texture size of " + maxSize);
// Round up depending on layer implementation to remove texture wastage
if (mTileLayer instanceof MultiTileLayer) {
// Round to the next multiple of the tile size, respecting maximum texture size
bufferSize = new IntSize(((mScreenSize.width + LayerController.MIN_BUFFER.width - 1) / TILE_SIZE.width + 1) * TILE_SIZE.width,
((mScreenSize.height + LayerController.MIN_BUFFER.height - 1) / TILE_SIZE.height + 1) * TILE_SIZE.height);
// Round to next power of two until we use NPOT texture support
IntSize bufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)),
Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height)));
} else {
int maxSize = getLayerController().getView().getMaxTextureSize();
// XXX Integrate gralloc/tiling work to circumvent this
if (mScreenSize.width > maxSize || mScreenSize.height > maxSize)
throw new RuntimeException("Screen size of " + mScreenSize + " larger than maximum texture size of " + maxSize);
// Round to next power of two until we have NPOT texture support
bufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)),
Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height)));
}
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,

View File

@ -61,8 +61,11 @@ public abstract class Layer {
mResolution = 1.0f;
}
/** Updates the layer. */
public final void update(GL10 gl) {
/**
* Updates the layer. This returns false if there is still work to be done
* after this update.
*/
public final boolean update(GL10 gl, RenderContext context) {
if (mTransactionLock.isHeldByCurrentThread()) {
throw new RuntimeException("draw() called while transaction lock held by this " +
"thread?!");
@ -70,11 +73,13 @@ public abstract class Layer {
if (mTransactionLock.tryLock()) {
try {
performUpdates(gl);
return performUpdates(gl, context);
} finally {
mTransactionLock.unlock();
}
}
return false;
}
/** Subclasses override this function to draw the layer. */
@ -158,9 +163,10 @@ public abstract class Layer {
/**
* Subclasses may override this method to perform custom layer updates. This will be called
* with the transaction lock held. Subclass implementations of this method must call the
* superclass implementation.
* superclass implementation. Returns false if there is still work to be done after this
* update is complete.
*/
protected void performUpdates(GL10 gl) {
protected boolean performUpdates(GL10 gl, RenderContext context) {
if (mNewOrigin != null) {
mOrigin = mNewOrigin;
mNewOrigin = null;
@ -169,6 +175,8 @@ public abstract class Layer {
mResolution = mNewResolution;
mNewResolution = 0.0f;
}
return true;
}
public static class RenderContext {

View File

@ -147,6 +147,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
LayerController controller = mView.getController();
RenderContext screenContext = createScreenContext();
boolean updated = true;
synchronized (controller) {
Layer rootLayer = controller.getRoot();
RenderContext pageContext = createPageContext();
@ -166,12 +168,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
mLastPageContext = pageContext;
/* Update layers. */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFrameRateLayer.update(gl);
mVertScrollLayer.update(gl);
mHorizScrollLayer.update(gl);
if (rootLayer != null) updated &= rootLayer.update(gl, pageContext);
updated &= mShadowLayer.update(gl, pageContext);
updated &= mCheckerboardLayer.update(gl, screenContext);
updated &= mFrameRateLayer.update(gl, screenContext);
updated &= mVertScrollLayer.update(gl, pageContext);
updated &= mHorizScrollLayer.update(gl, pageContext);
/* Draw the background. */
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
@ -220,6 +222,10 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
}
}
// If a layer update requires further work, schedule another redraw
if (!updated)
mView.requestRender();
PanningPerfAPI.recordFrameTime();
}

View File

@ -0,0 +1,284 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** 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 Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011-2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Lord <chrislord.net@gmail.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
* 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 ***** */
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.SingleTileLayer;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import javax.microedition.khronos.opengles.GL10;
/**
* Encapsulates the logic needed to draw a layer made of multiple tiles.
*
* TODO: Support repeating.
*/
public class MultiTileLayer extends Layer {
private static final String LOGTAG = "GeckoMultiTileLayer";
private final CairoImage mImage;
private IntSize mTileSize;
private IntSize mBufferSize;
private final ArrayList<SingleTileLayer> mTiles;
public MultiTileLayer(CairoImage image, IntSize tileSize) {
super();
mImage = image;
mTileSize = tileSize;
mBufferSize = new IntSize(0, 0);
mTiles = new ArrayList<SingleTileLayer>();
}
public void invalidate(Rect dirtyRect) {
if (!inTransaction())
throw new RuntimeException("invalidate() is only valid inside a transaction");
int x = 0, y = 0;
IntSize size = getSize();
for (SingleTileLayer layer : mTiles) {
Rect tileRect = new Rect(x, y, x + mTileSize.width, y + mTileSize.height);
if (tileRect.intersect(dirtyRect)) {
tileRect.offset(-x, -y);
layer.invalidate(tileRect);
}
x += mTileSize.width;
if (x >= size.width) {
x = 0;
y += mTileSize.height;
}
}
}
public void invalidate() {
for (SingleTileLayer layer : mTiles)
layer.invalidate();
}
@Override
public IntSize getSize() {
return mImage.getSize();
}
private void validateTiles() {
IntSize size = getSize();
if (size.equals(mBufferSize))
return;
// Regenerate tiles
mTiles.clear();
int offset = 0;
final int format = mImage.getFormat();
final ByteBuffer buffer = mImage.getBuffer().slice();
final int bpp = CairoUtils.bitsPerPixelForCairoFormat(format) / 8;
for (int y = 0; y < size.height; y += mTileSize.height) {
for (int x = 0; x < size.width; x += mTileSize.width) {
// Create a CairoImage implementation that returns a
// tile from the parent CairoImage. It's assumed that
// the tiles are stored in series.
final IntSize layerSize =
new IntSize(Math.min(mTileSize.width, size.width - x),
Math.min(mTileSize.height, size.height - y));
final int tileOffset = offset;
CairoImage subImage = new CairoImage() {
@Override
public ByteBuffer getBuffer() {
// Create a ByteBuffer that shares the data of the original
// buffer, but is positioned and limited so that only the
// tile data is accessible.
buffer.position(tileOffset);
ByteBuffer tileBuffer = buffer.slice();
tileBuffer.limit(layerSize.getArea() * bpp);
return tileBuffer;
}
@Override
public IntSize getSize() {
return layerSize;
}
@Override
public int getFormat() {
return format;
}
};
mTiles.add(new SingleTileLayer(subImage));
offset += layerSize.getArea() * bpp;
}
}
// Set tile origins and resolution
refreshTileMetrics(getOrigin(), getResolution(), false);
mBufferSize = size;
}
@Override
protected boolean performUpdates(GL10 gl, RenderContext context) {
super.performUpdates(gl, context);
validateTiles();
// Iterate over the tiles and decide which ones we'll be drawing
int dirtyTiles = 0;
boolean screenUpdateDone = false;
SingleTileLayer firstDirtyTile = null;
for (SingleTileLayer layer : mTiles) {
// First do a non-texture update to make sure coordinates are
// up-to-date.
boolean invalid = layer.getSkipTextureUpdate();
layer.setSkipTextureUpdate(true);
layer.performUpdates(gl, context);
RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
boolean isDirty = layer.isDirty();
if (isDirty) {
if (!RectF.intersects(layerBounds, context.viewport)) {
if (firstDirtyTile == null)
firstDirtyTile = layer;
dirtyTiles ++;
invalid = true;
} else {
// This tile intersects with the screen and is dirty,
// update it immediately.
layer.setSkipTextureUpdate(false);
screenUpdateDone = true;
layer.performUpdates(gl, context);
invalid = false;
}
}
// We use the SkipTextureUpdate flag as a marker of a tile's
// validity. This is required, as sometimes layers are drawn
// without updating first, and we mustn't draw tiles that have
// been marked as invalid that we haven't updated.
layer.setSkipTextureUpdate(invalid);
}
// Now if no tiles that intersect with the screen were updated, update
// a single tile that doesn't (if there are any). This has the effect
// of spreading out non-critical texture upload over time, and smoothing
// upload-related hitches.
if (!screenUpdateDone && firstDirtyTile != null) {
firstDirtyTile.setSkipTextureUpdate(false);
firstDirtyTile.performUpdates(gl, context);
dirtyTiles --;
}
return (dirtyTiles == 0);
}
private void refreshTileMetrics(Point origin, float resolution, boolean inTransaction) {
int x = 0, y = 0;
IntSize size = getSize();
for (SingleTileLayer layer : mTiles) {
if (!inTransaction)
layer.beginTransaction(null);
if (origin != null)
layer.setOrigin(new Point(origin.x + x, origin.y + y));
if (resolution >= 0.0f)
layer.setResolution(resolution);
if (!inTransaction)
layer.endTransaction();
x += mTileSize.width;
if (x >= size.width) {
x = 0;
y += mTileSize.height;
}
}
}
@Override
public void setOrigin(Point newOrigin) {
super.setOrigin(newOrigin);
refreshTileMetrics(newOrigin, -1, true);
}
@Override
public void setResolution(float newResolution) {
super.setResolution(newResolution);
refreshTileMetrics(null, newResolution, true);
}
@Override
public void beginTransaction(LayerView aView) {
super.beginTransaction(aView);
for (SingleTileLayer layer : mTiles)
layer.beginTransaction(aView);
}
@Override
public void endTransaction() {
for (SingleTileLayer layer : mTiles)
layer.endTransaction();
super.endTransaction();
}
@Override
public void draw(RenderContext context) {
for (SingleTileLayer layer : mTiles) {
// We use the SkipTextureUpdate flag as a validity flag. If it's false,
// the contents of this tile are invalid and we shouldn't draw it.
if (layer.getSkipTextureUpdate())
continue;
// Avoid work, only draw tiles that intersect with the viewport
RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
if (RectF.intersects(layerBounds, context.viewport))
layer.draw(context);
}
}
}

View File

@ -59,12 +59,14 @@ public abstract class TileLayer extends Layer {
private final CairoImage mImage;
private final boolean mRepeat;
private IntSize mSize;
private boolean mSkipTextureUpdate;
private int[] mTextureIDs;
public TileLayer(boolean repeat, CairoImage image) {
mRepeat = repeat;
mImage = image;
mSize = new IntSize(0, 0);
mSkipTextureUpdate = false;
IntSize bufferSize = mImage.getSize();
mDirtyRect = new Rect();
@ -98,6 +100,10 @@ public abstract class TileLayer extends Layer {
invalidate(new Rect(0, 0, bufferSize.width, bufferSize.height));
}
public boolean isDirty() {
return mImage.getSize().isPositive() && (mTextureIDs == null || !mDirtyRect.isEmpty());
}
private void validateTexture(GL10 gl) {
/* Calculate the ideal texture size. This must be a power of two if
* the texture is repeated or OpenGL ES 2.0 isn't supported, as
@ -126,16 +132,28 @@ public abstract class TileLayer extends Layer {
}
}
/** Tells the tile not to update the texture on the next update. */
public void setSkipTextureUpdate(boolean skip) {
mSkipTextureUpdate = skip;
}
public boolean getSkipTextureUpdate() {
return mSkipTextureUpdate;
}
@Override
protected void performUpdates(GL10 gl) {
super.performUpdates(gl);
protected boolean performUpdates(GL10 gl, RenderContext context) {
super.performUpdates(gl, context);
if (mSkipTextureUpdate)
return false;
// Reallocate the texture if the size has changed
validateTexture(gl);
// Don't do any work if the image has an invalid size.
if (!mImage.getSize().isPositive())
return;
return true;
// If we haven't allocated a texture, assume the whole region is dirty
if (mTextureIDs == null)
@ -144,6 +162,8 @@ public abstract class TileLayer extends Layer {
uploadDirtyRect(gl, mDirtyRect);
mDirtyRect.setEmpty();
return true;
}
private void uploadFullTexture(GL10 gl) {

View File

@ -77,8 +77,8 @@ public class WidgetTileLayer extends Layer {
}
@Override
protected void performUpdates(GL10 gl) {
super.performUpdates(gl);
protected boolean performUpdates(GL10 gl, RenderContext context) {
super.performUpdates(gl, context);
if (mTextureIDs == null) {
mTextureIDs = new int[1];
@ -87,6 +87,8 @@ public class WidgetTileLayer extends Layer {
bindAndSetGLParameters();
GeckoAppShell.bindWidgetTexture();
return true;
}
@Override

View File

@ -27,9 +27,7 @@
<!ENTITY awesomebar_default_text "Enter Search or Address">
<!ENTITY bookmarks_title "Bookmarks">
<!ENTITY bookmark_add "Bookmark">
<!ENTITY bookmark_remove "Remove">
<!ENTITY bookmark "Bookmark">
<!ENTITY bookmark_added "Bookmark added">
<!ENTITY bookmark_removed "Bookmark removed">
@ -72,6 +70,7 @@
<!ENTITY quit "Quit">
<!ENTITY addons "Add-ons">
<!ENTITY downloads "Downloads">
<!ENTITY share "Share">
<!ENTITY save_as_pdf "Save as PDF">

View File

@ -11,7 +11,7 @@
<item android:id="@+id/bookmark"
android:icon="@drawable/ic_menu_bookmark_add"
android:title="@string/bookmark_add"/>
android:title="@string/bookmark"/>
<item android:id="@+id/share"
android:icon="@drawable/ic_menu_share"
@ -33,6 +33,9 @@
<item android:id="@+id/addons"
android:title="@string/addons"/>
<item android:id="@+id/downloads"
android:title="@string/downloads"/>
<item android:id="@+id/quit"
android:title="@string/quit" />
</menu>

View File

@ -35,9 +35,7 @@
<string name="awesomebar_default_text">&awesomebar_default_text;</string>
<string name="quit">&quit;</string>
<string name="bookmarks_title">&bookmarks_title;</string>
<string name="bookmark_add">&bookmark_add;</string>
<string name="bookmark_remove">&bookmark_remove;</string>
<string name="bookmark">&bookmark;</string>
<string name="bookmark_added">&bookmark_added;</string>
<string name="bookmark_removed">&bookmark_removed;</string>
@ -80,6 +78,7 @@
<string name="forward">&forward;</string>
<string name="new_tab">&new_tab;</string>
<string name="addons">&addons;</string>
<string name="downloads">&downloads;</string>
<string name="site_settings_title">&site_settings_title;</string>
<string name="site_settings_cancel">&site_settings_cancel;</string>

View File

@ -5,6 +5,7 @@ import com.jayway.android.robotium.solo.Solo;
import @ANDROID_PACKAGE_NAME@.*;
import android.app.Activity;
import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
import android.content.Intent;
import java.util.HashMap;
@ -47,7 +48,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
mActivity = getActivity();
// Set up Robotium.solo and Driver objects
mSolo = new Solo(getInstrumentation(), getActivity());
mSolo = new Solo(getInstrumentation());
mDriver = new FennecNativeDriver(mActivity, mSolo);
mActions = new FennecNativeActions(mActivity, mSolo, getInstrumentation());
mDriver.setLogFile((String)config.get("logfile"));
@ -67,30 +68,42 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
super.tearDown();
}
protected final Activity getActivityFromClick(Element element) {
Instrumentation inst = getInstrumentation();
Instrumentation.ActivityMonitor monitor = inst.addMonitor((String)null, null, false);
element.click();
return inst.waitForMonitor(monitor);
}
protected final void enterUrl(String url) {
mActions.waitForGeckoEvent("Gecko:Ready");
Element awesomebar = mDriver.findElement("awesome_bar");
awesomebar.click();
mActions.expectGeckoEvent("Gecko:Ready").blockForEvent();
Element awesomebar = mDriver.findElement(mActivity, "awesome_bar");
Activity awesomeBarActivity = getActivityFromClick(awesomebar);
getInstrumentation().waitForIdleSync();
Element urlbar = mDriver.findElement("awesomebar_text");
Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text");
mActions.sendKeys(url);
mAsserter.is(urlbar.getText(), url, "Awesomebar URL typed properly");
}
protected final void loadUrl(String url) {
enterUrl(url);
protected final void hitEnterAndWait() {
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
mActions.sendSpecialKey(Actions.SpecialKey.ENTER);
// wait for screen to load
mActions.waitForGeckoEvent("DOMContentLoaded");
contentEventExpecter.blockForEvent();
}
protected final void loadUrl(String url) {
enterUrl(url);
hitEnterAndWait();
}
protected final void verifyUrl(String url) {
Element awesomebar = mDriver.findElement("awesome_bar");
Element urlbar = mDriver.findElement("awesomebar_text");
awesomebar.click();
Element awesomebar = mDriver.findElement(mActivity, "awesome_bar");
Activity awesomeBarActivity = getActivityFromClick(awesomebar);
getInstrumentation().waitForIdleSync();
Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text");
mAsserter.is(urlbar.getText(), url, "Awesomebar URL stayed the same");
}
}

View File

@ -11,8 +11,7 @@ public class testBookmark extends BaseTest {
//Click the top item in the list.
mActions.sendSpecialKey(Actions.SpecialKey.DOWN);
mActions.sendSpecialKey(Actions.SpecialKey.ENTER);
mActions.waitForGeckoEvent("DOMContentLoaded");
hitEnterAndWait();
verifyUrl(URL);
@ -23,8 +22,8 @@ public class testBookmark extends BaseTest {
getInstrumentation().waitForIdleSync();
mActions.sendSpecialKey(Actions.SpecialKey.DOWN);
mActions.sendSpecialKey(Actions.SpecialKey.DOWN);
mActions.sendSpecialKey(Actions.SpecialKey.ENTER);
mActions.waitForGeckoEvent("DOMContentLoaded");
hitEnterAndWait();
getInstrumentation().waitForIdleSync();
//Unfortunately, the item isn't constant so can't be tested.

View File

@ -2,36 +2,31 @@
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
import android.app.Activity;
public class testNewTab extends BaseTest {
public void testNewTab() {
// TODO: find a better way to not hardcode this url
String url = "http://mochi.test:8888/tests/robocop/robocop_blank_01.html";
String url2 = "http://mochi.test:8888/tests/robocop/robocop_blank_02.html";
mActions.waitForGeckoEvent("Gecko:Ready");
Element tabs = mDriver.findElement("tabs");
mActions.expectGeckoEvent("Gecko:Ready").blockForEvent();
Element tabs = mDriver.findElement(getActivity(), "tabs");
// Add one tab
tabs.click();
Activity awesomeBarActivity = getActivityFromClick(tabs);
Element urlbar = mDriver.findElement("awesomebar_text");
getInstrumentation().waitForIdleSync();
Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text");
mActions.sendKeys(url);
mAsserter.is(urlbar.getText(), url, "Awesomebar url is fine");
mActions.sendSpecialKey(Actions.SpecialKey.ENTER);
mActions.waitForGeckoEvent("DOMContentLoaded");
try {
Thread.sleep(5000);
} catch (Throwable e) {
}
hitEnterAndWait();
// See tab count
Element tabCount = mDriver.findElement("tabs_count");
Element tabCount = mDriver.findElement(getActivity(), "tabs_count");
mAsserter.is(tabCount.getText(), "2", "Number of tabs has increased");
// Click tab list
tabs.click();
Element addTab = mDriver.findElement("add_tab");
Activity tabTray = getActivityFromClick(tabs);
Element addTab = mDriver.findElement(tabTray, "add_tab");
//Add another tab
addTab.click();
@ -40,8 +35,7 @@ public class testNewTab extends BaseTest {
getInstrumentation().waitForIdleSync();
mAsserter.is(urlbar.getText(), url2, "URL is still fine");
mActions.sendSpecialKey(Actions.SpecialKey.ENTER);
mActions.waitForGeckoEvent("DOMContentLoaded");
hitEnterAndWait();
//Check tab count another time.
mAsserter.is(tabCount.getText(), "3", "Number of tabs has increased");
}

View File

@ -125,6 +125,7 @@ var Downloads = {
},
observe: function dl_observe(aSubject, aTopic, aData) {
let download = aSubject.QueryInterface(Ci.nsIDownload);
let msgKey = "";
if (aTopic == "dl-start") {
msgKey = "alertDownloadsStart";
@ -138,12 +139,21 @@ var Downloads = {
NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long");
} else if (aTopic == "dl-done") {
msgKey = "alertDownloadsDone";
let message = {
gecko: {
type: "Downloads:Done",
displayName: download.displayName,
path: download.targetFile.path,
size: download.size,
mimeType: download.MIMEInfo ? download.MIMEInfo.type : ""
}
};
sendMessageToJava(message);
}
if (msgKey) {
let download = aSubject.QueryInterface(Ci.nsIDownload);
if (msgKey)
this.showAlert(download, Strings.browser.formatStringFromName(msgKey, [download.displayName], 1));
}
},
QueryInterface: function (aIID) {

View File

@ -7,9 +7,7 @@ Cu.import("resource://gre/modules/Services.jsm");
function dump(a) {
Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService)
.logStringMessage(a);
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
}
function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) {
@ -45,10 +43,7 @@ BrowserCLH.prototype = {
let urlParam = "about:home";
try {
urlParam = aCmdLine.handleFlagWithParam("remote", false);
} catch (e) {
// Optional so not a real error
}
dump("fs_handle: " + urlParam);
} catch (e) { /* Optional */ }
try {
let uri = resolveURIInternal(aCmdLine, urlParam);
@ -57,19 +52,14 @@ BrowserCLH.prototype = {
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
if (browserWin) {
browserWin.browserDOMWindow.openURI(uri,
null,
Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW,
Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
browserWin.browserDOMWindow.openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
} else {
browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", urlParam);
}
aCmdLine.preventDefault = true;
} catch (x) {
Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService)
.logStringMessage("fs_handle exception!: " + x);
dump("BrowserCLH.handle: " + x);
}
},

View File

@ -75,6 +75,7 @@
#include "mozilla/Util.h" // for DebugOnly
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
#include "nsITimer.h"
#include "mozilla/FunctionTimer.h"
@ -152,6 +153,7 @@ public:
, mDiskCacheEnabled(false)
, mDiskCacheCapacity(0)
, mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
, mSmartSizeEnabled(false)
, mOfflineCacheEnabled(false)
, mOfflineCacheCapacity(0)
, mMemoryCacheEnabled(true)
@ -175,6 +177,7 @@ public:
void SetDiskCacheCapacity(PRInt32);
PRInt32 DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; }
nsILocalFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
bool SmartSizeEnabled() { return mSmartSizeEnabled; }
bool OfflineCacheEnabled();
PRInt32 OfflineCacheCapacity() { return mOfflineCacheCapacity; }
@ -199,6 +202,7 @@ private:
PRInt32 mDiskCacheCapacity; // in kilobytes
PRInt32 mDiskCacheMaxEntrySize; // in kilobytes
nsCOMPtr<nsILocalFile> mDiskCacheParentDirectory;
bool mSmartSizeEnabled;
bool mOfflineCacheEnabled;
PRInt32 mOfflineCacheCapacity; // in kilobytes
@ -218,6 +222,23 @@ private:
NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheProfilePrefObserver, nsIObserver)
class nsSetDiskSmartSizeCallback : public nsITimerCallback
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Notify(nsITimer* aTimer) {
if (nsCacheService::gService) {
nsCacheServiceAutoLock autoLock;
nsCacheService::gService->SetDiskSmartSize_Locked();
nsCacheService::gService->mSmartSizeTimer = nsnull;
}
return NS_OK;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsSetDiskSmartSizeCallback, nsITimerCallback)
// Runnable sent to main thread after the cache IO thread calculates available
// disk space, so that there is no race in setting mDiskCacheCapacity.
class nsSetSmartSizeEvent: public nsRunnable
@ -228,32 +249,27 @@ public:
NS_IMETHOD Run()
{
nsresult rv;
NS_ASSERTION(NS_IsMainThread(),
"Setting smart size data off the main thread");
// Main thread may have already called nsCacheService::Shutdown
if (!nsCacheService::gService || !nsCacheService::gService->mObserver)
return NS_ERROR_NOT_AVAILABLE;
bool smartSizeEnabled;
nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!branch) {
NS_WARNING("Failed to get pref service!");
return NS_ERROR_NOT_AVAILABLE;
}
// ensure smart sizing wasn't switched off while event was pending
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
if (NS_FAILED(rv))
smartSizeEnabled = false;
if (smartSizeEnabled) {
nsCacheService::SetDiskCacheCapacity(mSmartSize);
rv = branch->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize);
if (NS_FAILED(rv))
NS_WARNING("Failed to set smart size pref");
}
return rv;
// Ensure smart sizing wasn't switched off while event was pending.
// It is safe to access the observer without the lock since we are
// on the main thread and the value changes only on the main thread.
if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
return NS_OK;
nsCacheService::SetDiskCacheCapacity(mSmartSize);
nsCOMPtr<nsIPrefBranch2> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!ps ||
NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
NS_WARNING("Failed to set smart size pref");
return NS_OK;
}
private:
@ -445,13 +461,12 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
// Update the cache capacity when smart sizing is turned on/off
} else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
// Is the update because smartsizing was turned on, or off?
bool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
&mSmartSizeEnabled);
if (NS_FAILED(rv))
return rv;
PRInt32 newCapacity = 0;
if (smartSizeEnabled) {
if (mSmartSizeEnabled) {
nsCacheService::SetDiskSmartSize();
} else {
// Smart sizing switched off: use user specified size
@ -671,21 +686,22 @@ nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
// of 50 MB, then keep user's value. Otherwise use smart sizing.
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
false);
return false;
mSmartSizeEnabled = false;
branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
mSmartSizeEnabled);
return mSmartSizeEnabled;
}
}
// Set manual setting to MAX cache size as starting val for any
// adjustment by user: (bug 559942 comment 65)
branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
}
bool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
if (NS_FAILED(rv))
return false;
return !!smartSizeEnabled;
&mSmartSizeEnabled);
if (NS_FAILED(rv))
mSmartSizeEnabled = false;
return mSmartSizeEnabled;
}
@ -758,20 +774,12 @@ nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
// Avoid evictions: use previous cache size until smart size event
// updates mDiskCacheCapacity
if (!firstSmartSizeRun) {
PRInt32 oldSmartSize;
rv = branch->GetIntPref(DISK_CACHE_SMART_SIZE_PREF,
&oldSmartSize);
mDiskCacheCapacity = oldSmartSize;
} else {
PRInt32 oldCapacity;
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
if (NS_SUCCEEDED(rv)) {
mDiskCacheCapacity = oldCapacity;
} else {
mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
}
}
rv = branch->GetIntPref(firstSmartSizeRun ?
DISK_CACHE_CAPACITY_PREF :
DISK_CACHE_SMART_SIZE_PREF,
&mDiskCacheCapacity);
if (NS_FAILED(rv))
mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
}
if (firstSmartSizeRun) {
@ -1142,6 +1150,11 @@ nsCacheService::Shutdown()
ClearDoomList();
ClearActiveEntries();
if (mSmartSizeTimer) {
mSmartSizeTimer->Cancel();
mSmartSizeTimer = nsnull;
}
// Make sure to wait for any pending cache-operations before
// proceeding with destructive actions (bug #620660)
(void) SyncWithCacheIOThread();
@ -1447,7 +1460,22 @@ nsCacheService::CreateDiskDevice()
mDiskDevice = nsnull;
}
SetDiskSmartSize_Locked(true);
// Disk device is usually created during the startup. Delay smart size
// calculation to avoid possible massive IO caused by eviction of entries
// in case the new smart size is smaller than current cache usage.
if (!mSmartSizeTimer) {
mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_FAILED(rv))
return rv;
rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
1000*60*3,
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to post smart size timer");
mSmartSizeTimer = nsnull;
}
}
return rv;
}
@ -2652,11 +2680,11 @@ nsCacheService::SetDiskSmartSize()
if (!gService) return NS_ERROR_NOT_AVAILABLE;
return gService->SetDiskSmartSize_Locked(false);
return gService->SetDiskSmartSize_Locked();
}
nsresult
nsCacheService::SetDiskSmartSize_Locked(bool checkPref)
nsCacheService::SetDiskSmartSize_Locked()
{
nsresult rv;
@ -2666,17 +2694,8 @@ nsCacheService::SetDiskSmartSize_Locked(bool checkPref)
if (!mDiskDevice)
return NS_ERROR_NOT_AVAILABLE;
if (checkPref) {
nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!branch) return NS_ERROR_FAILURE;
bool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
if (NS_FAILED(rv) || !smartSizeEnabled)
return NS_ERROR_NOT_AVAILABLE;
}
if (!mObserver->SmartSizeEnabled())
return NS_ERROR_NOT_AVAILABLE;
nsAutoString cachePath;
rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);

View File

@ -63,6 +63,7 @@ class nsDiskCacheDevice;
class nsMemoryCacheDevice;
class nsOfflineCacheDevice;
class nsCacheServiceAutoLock;
class nsITimer;
/******************************************************************************
@ -196,6 +197,7 @@ private:
friend class nsProcessRequestEvent;
friend class nsSetSmartSizeEvent;
friend class nsBlockOnCacheThreadEvent;
friend class nsSetDiskSmartSizeCallback;
/**
* Internal Methods
@ -264,7 +266,7 @@ private:
void LogCacheStatistics();
#endif
nsresult SetDiskSmartSize_Locked(bool checkPref);
nsresult SetDiskSmartSize_Locked();
/**
* Data Members
@ -280,6 +282,7 @@ private:
nsCOMPtr<nsIThread> mCacheIOThread;
nsTArray<nsISupports*> mDoomedObjects;
nsCOMPtr<nsITimer> mSmartSizeTimer;
bool mInitialized;

View File

@ -118,6 +118,22 @@ private:
nsDiskCacheBinding *mBinding;
};
class nsEvictDiskCacheEntriesEvent : public nsRunnable {
public:
nsEvictDiskCacheEntriesEvent(nsDiskCacheDevice *device)
: mDevice(device) {}
NS_IMETHOD Run()
{
nsCacheServiceAutoLock lock;
mDevice->EvictDiskCacheEntries(mDevice->mCacheCapacity);
return NS_OK;
}
private:
nsDiskCacheDevice *mDevice;
};
/******************************************************************************
* nsDiskCacheEvictor
*
@ -1120,8 +1136,14 @@ nsDiskCacheDevice::SetCapacity(PRUint32 capacity)
// Units are KiB's
mCacheCapacity = capacity;
if (Initialized()) {
// start evicting entries if the new size is smaller!
EvictDiskCacheEntries(mCacheCapacity);
if (NS_IsMainThread()) {
// Do not evict entries on the main thread
nsCacheService::DispatchToCacheIOThread(
new nsEvictDiskCacheEntriesEvent(this));
} else {
// start evicting entries if the new size is smaller!
EvictDiskCacheEntries(mCacheCapacity);
}
}
// Let cache map know of the new capacity
mCacheMap.NotifyCapacityChange(capacity);

View File

@ -107,6 +107,7 @@ public:
private:
friend class nsDiskCacheDeviceDeactivateEntryEvent;
friend class nsEvictDiskCacheEntriesEvent;
/**
* Private methods
*/

View File

@ -1017,7 +1017,7 @@ nsCookieService::TryInitDB(bool aRecreateDB)
// Use write-ahead-logging for performance. We cap the autocheckpoint limit at
// 16 pages (around 500KB).
mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA journal_mode = WAL"));
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = WAL"));
mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA wal_autocheckpoint = 16"));

View File

@ -113,6 +113,8 @@ nsHttpPipeline::~nsHttpPipeline()
// make sure we aren't still holding onto any transactions!
Close(NS_ERROR_ABORT);
NS_IF_RELEASE(mConnection);
if (mPushBackBuf)
free(mPushBackBuf);
}
@ -125,7 +127,7 @@ nsHttpPipeline::AddTransaction(nsAHttpTransaction *trans)
NS_ADDREF(trans);
mRequestQ.AppendElement(trans);
if (mConnection) {
if (mConnection && !mClosed) {
trans->SetConnection(this);
if (mRequestQ.Length() == 1)
@ -700,10 +702,6 @@ nsHttpPipeline::Close(nsresult reason)
mResponseQ.Clear();
}
// we must no longer reference the connection! This needs to come
// after we've closed all our transactions, since they might want
// connection info as they close.
NS_IF_RELEASE(mConnection);
}
nsresult

View File

@ -192,4 +192,9 @@ protected:
nsCOMPtr<mozIStorageStatement> mStatement;
};
// Use this to make queries uniquely identifiable in telemetry
// statistics, especially PRAGMAs. We don't include __LINE__ so that
// queries are stable in the face of source code changes.
#define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ "
#endif /* MOZSTORAGEHELPER_H */

View File

@ -52,6 +52,7 @@
#include "mozIStorageAsyncStatement.h"
#include "mozIStoragePendingStatement.h"
#include "mozIStorageError.h"
#include "mozStorageHelper.h"
#define OBSERVER_TOPIC_IDLE_DAILY "idle-daily"
#define OBSERVER_TOPIC_XPCOM_SHUTDOWN "xpcom-shutdown"
@ -221,7 +222,7 @@ Vacuumer::execute()
{
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"PRAGMA page_size"
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"
), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, false);
bool hasResult;
@ -240,7 +241,7 @@ Vacuumer::execute()
{
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"PRAGMA journal_mode"
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode"
), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, false);
bool hasResult;
@ -300,7 +301,7 @@ Vacuumer::execute()
if (canOptimizePageSize) {
nsCOMPtr<mozIStorageAsyncStatement> pageSizeStmt;
rv = mDBConn->CreateAsyncStatement(nsPrintfCString(
"PRAGMA page_size = %ld", expectedPageSize
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size = %ld", expectedPageSize
), getter_AddRefs(pageSizeStmt));
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<BaseCallback> callback = new BaseCallback();

View File

@ -70,6 +70,7 @@
#include "StorageBaseStatementInternal.h"
#include "SQLCollations.h"
#include "FileSystemModule.h"
#include "mozStorageHelper.h"
#include "prlog.h"
#include "prprf.h"
@ -650,14 +651,17 @@ Connection::initialize(nsIFile *aDatabaseFile,
// the database has just been created, otherwise, if the database does not
// use WAL journal mode, a VACUUM operation will updated its page_size.
PRInt64 pageSize = DEFAULT_PAGE_SIZE;
nsCAutoString pageSizeQuery("PRAGMA page_size = ");
nsCAutoString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA page_size = ");
pageSizeQuery.AppendInt(pageSize);
rv = ExecuteSimpleSQL(pageSizeQuery);
NS_ENSURE_SUCCESS(rv, rv);
// Get the current page_size, since it may differ from the specified value.
sqlite3_stmt *stmt;
srv = prepareStatement(NS_LITERAL_CSTRING("PRAGMA page_size"), &stmt);
NS_NAMED_LITERAL_CSTRING(pragma_page_size,
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size");
srv = prepareStatement(pragma_page_size, &stmt);
if (srv == SQLITE_OK) {
if (SQLITE_ROW == stepStatement(stmt)) {
pageSize = ::sqlite3_column_int64(stmt, 0);
@ -668,7 +672,8 @@ Connection::initialize(nsIFile *aDatabaseFile,
// Setting the cache_size forces the database open, verifying if it is valid
// or corrupt. So this is executed regardless it being actually needed.
// The cache_size is calculated from the actual page_size, to save memory.
nsCAutoString cacheSizeQuery("PRAGMA cache_size = ");
nsCAutoString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA cache_size = ");
cacheSizeQuery.AppendInt(NS_MIN(DEFAULT_CACHE_SIZE_PAGES,
PRInt32(MAX_CACHE_SIZE_BYTES / pageSize)));
srv = ::sqlite3_exec(mDBConn, cacheSizeQuery.get(), NULL, NULL, NULL);

View File

@ -798,11 +798,12 @@ toolbar#nav-bar {
self.automation.log.warning("TEST-UNEXPECTED-FAIL | invalid setup: missing mochikit extension")
return None
# Support Firefox (browser) and SeaMonkey (navigator).
# Support Firefox (browser), B2G (shell) and SeaMonkey (navigator).
chrome = ""
if options.browserChrome or options.chrome or options.a11y:
chrome += """
overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://browser/content/shell.xul chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://navigator/content/navigator.xul chrome://mochikit/content/browser-test-overlay.xul
"""

View File

@ -280,6 +280,7 @@ class MochiRemote(Mochitest):
def buildProfile(self, options):
manifest = Mochitest.buildProfile(self, options)
self.localProfile = options.profilePath
self._dm.removeDir(self.remoteProfile)
if self._dm.pushDir(options.profilePath, self.remoteProfile) == None:
raise devicemanager.FileError("Unable to copy profile to device.")

View File

@ -233,7 +233,8 @@ SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
}
nsCOMPtr<mozIStorageStatement> statement;
nsCAutoString query("PRAGMA journal_mode = ");
nsCAutoString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA journal_mode = ");
query.Append(journalMode);
aDBConn->CreateStatement(query, getter_AddRefs(statement));
NS_ENSURE_TRUE(statement, JOURNAL_DELETE);
@ -551,7 +552,7 @@ Database::InitSchema(bool* aDatabaseMigrated)
// database file already existed with a different page size.
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
"PRAGMA page_size"
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"
), getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
bool hasResult = false;
@ -563,7 +564,7 @@ Database::InitSchema(bool* aDatabaseMigrated)
// Ensure that temp tables are held in memory, not on disk.
nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA temp_store = MEMORY"));
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY"));
NS_ENSURE_SUCCESS(rv, rv);
// Get the current database size. Due to chunked growth we have to use
@ -593,7 +594,8 @@ Database::InitSchema(bool* aDatabaseMigrated)
// Set the number of cached pages.
// We don't use PRAGMA default_cache_size, since the database could be moved
// among different devices and the value would adapt accordingly.
nsCAutoString cacheSizePragma("PRAGMA cache_size = ");
nsCAutoString cacheSizePragma(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA cache_size = ");
cacheSizePragma.AppendInt(cacheSize / mDBPageSize);
rv = mMainConn->ExecuteSimpleSQL(cacheSizePragma);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -908,11 +908,11 @@ let PlacesDBUtils = {
}
},
PLACES_DATABASE_PAGESIZE_B: "PRAGMA page_size",
PLACES_DATABASE_PAGESIZE_B: "PRAGMA page_size /* PlacesDBUtils.jsm PAGESIZE_B */",
PLACES_DATABASE_SIZE_PER_PAGE_B: function() {
// Cannot use the filesize here, due to chunked growth.
let stmt = DBConn.createStatement("PRAGMA page_size");
let stmt = DBConn.createStatement("PRAGMA page_size /* PlacesDBUtils.jsm SIZE_PER_PAGE_B */");
stmt.executeStep();
let pageSize = stmt.row.page_size;
stmt.finalize();

View File

@ -3126,7 +3126,8 @@ nsUrlClassifierDBServiceWorker::SetCacheSize(
NS_ASSERTION(hasResult, "Should always be able to get page size from sqlite");
PRUint32 pageSize = mGetPageSizeStatement->AsInt32(0);
PRUint32 cachePages = aCacheSize / pageSize;
nsCAutoString cacheSizePragma("PRAGMA cache_size=");
nsCAutoString cacheSizePragma(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA cache_size=");
cacheSizePragma.AppendInt(cachePages);
rv = aConnection->ExecuteSimpleSQL(cacheSizePragma);
NS_ENSURE_SUCCESS(rv, rv);
@ -3413,7 +3414,7 @@ nsUrlClassifierDBServiceWorker::OpenDb()
NS_ENSURE_SUCCESS(rv, rv);
rv = connection->CreateStatement
(NS_LITERAL_CSTRING("PRAGMA page_size"),
(NS_LITERAL_CSTRING(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"),
getter_AddRefs(mGetPageSizeStatement));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -1279,8 +1279,20 @@
var self = this;
this.muteButton.addEventListener("command", function() { self.toggleMute(); }, false);
this.playButton.addEventListener("command", function() { self.togglePause(); }, false);
this.controlsSpacer.addEventListener("click", function(e) { if (e.button == 0 && !self.hasError()) { self.togglePause(); } }, false);
this.fullscreenButton.addEventListener("command", function() { self.toggleFullscreen(); }, false );
this.controlsSpacer.addEventListener("click", function spacerClickHandler(e) {
if (e.button != 0 || self.hasError())
return;
// Read defaultPrevented asynchronously, since Web content
// may want to consume the "click" event but will only
// receive it after us (bug 693014).
setTimeout(function togglePauseCallback() {
if (!e.defaultPrevented)
self.togglePause();
}, 0);
}, false);
if (!this.isAudioOnly) {
this.muteButton.addEventListener("mouseover", function(e) { self.onVolumeMouseInOut(e); }, false);
this.muteButton.addEventListener("mouseout", function(e) { self.onVolumeMouseInOut(e); }, false);

View File

@ -823,9 +823,10 @@ var gViewController = {
isEnabled: function(aAddon) {
return !!aAddon && (gViewController.currentViewObj != gDetailView);
},
doCommand: function(aAddon) {
doCommand: function(aAddon, aScrollToPreferences) {
gViewController.loadView("addons://detail/" +
encodeURIComponent(aAddon.id));
encodeURIComponent(aAddon.id) +
(aScrollToPreferences ? "/preferences" : ""));
}
},
@ -964,7 +965,7 @@ var gViewController = {
},
doCommand: function(aAddon) {
if (aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE) {
gViewController.commands.cmd_showItemDetails.doCommand(aAddon);
gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);
return;
}
var optionsURL = aAddon.optionsURL;
@ -2565,7 +2566,7 @@ var gDetailView = {
}
},
_updateView: function(aAddon, aIsRemote) {
_updateView: function(aAddon, aIsRemote, aScrollToPreferences) {
this._updatePrefs.addObserver("", this, false);
this.clearLoading();
@ -2738,7 +2739,7 @@ var gDetailView = {
}
}
this.fillSettingsRows();
this.fillSettingsRows(aScrollToPreferences);
this.updateState();
@ -2747,6 +2748,13 @@ var gDetailView = {
},
show: function(aAddonId, aRequest) {
let index = aAddonId.indexOf("/preferences");
let scrollToPreferences = false;
if (index >= 0) {
aAddonId = aAddonId.substring(0, index);
scrollToPreferences = true;
}
var self = this;
this._loadingTimer = setTimeout(function() {
self.node.setAttribute("loading-extended", true);
@ -2759,7 +2767,7 @@ var gDetailView = {
return;
if (aAddon) {
self._updateView(aAddon, false);
self._updateView(aAddon, false, scrollToPreferences);
return;
}
@ -2878,7 +2886,7 @@ var gDetailView = {
rows.removeChild(rows.lastChild);
},
fillSettingsRows: function () {
fillSettingsRows: function (aScrollToPreferences) {
this.emptySettingsRows();
if (this._addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE)
return;
@ -2952,11 +2960,31 @@ var gDetailView = {
if (firstSetting)
firstSetting.clientTop;
Services.obs.notifyObservers(document, "addon-options-displayed", gDetailView._addon.id);
if (aScrollToPreferences)
gDetailView.scrollToPreferencesRows();
}, false);
} else {
if (firstSetting)
firstSetting.clientTop;
Services.obs.notifyObservers(document, "addon-options-displayed", this._addon.id);
if (aScrollToPreferences)
gDetailView.scrollToPreferencesRows();
}
},
scrollToPreferencesRows: function() {
// We find this row, rather than remembering it from above,
// in case it has been changed by the observers.
let firstRow = gDetailView.node.querySelector('setting[first-row="true"]');
if (firstRow) {
let top = firstRow.boxObject.y;
top -= parseInt(window.getComputedStyle(firstRow, null).getPropertyValue("margin-top"));
let detailViewBoxObject = gDetailView.node.boxObject;
top -= detailViewBoxObject.y;
detailViewBoxObject.QueryInterface(Ci.nsIScrollBoxObject);
detailViewBoxObject.scrollTo(0, top);
}
},

View File

@ -23,6 +23,9 @@ var observer = {
var setting = aSubject.querySelector("rows > setting[first-row] ~ setting");
var input = gManagerWindow.document.getAnonymousElementByAttribute(setting, "class", "setting-label");
isnot(input, null, "XBL binding should be applied");
// Add some extra height to the scrolling pane to ensure that it needs to scroll when appropriate.
gManagerWindow.document.getElementById("detail-controls").style.marginBottom = "1000px";
}
}
};
@ -39,6 +42,17 @@ function installAddon(aCallback) {
}, "application/x-xpinstall");
}
function checkScrolling(aShouldHaveScrolled) {
var detailView = gManagerWindow.document.getElementById("detail-view");
var boxObject = detailView.boxObject;
boxObject.QueryInterface(Ci.nsIScrollBoxObject);
ok(detailView.scrollHeight > boxObject.height, "Page should require scrolling");
if (aShouldHaveScrolled)
isnot(detailView.scrollTop, 0, "Page should have scrolled");
else
is(detailView.scrollTop, 0, "Page should not have scrolled");
}
function test() {
waitForExplicitFinish();
@ -147,14 +161,15 @@ add_test(function() {
wait_for_view_load(gManagerWindow, function() {
is(observer.lastData, "inlinesettings1@tests.mozilla.org", "Observer notification should have fired");
is(gManagerWindow.gViewController.currentViewId,
"addons://detail/inlinesettings1%40tests.mozilla.org/preferences",
"Current view should scroll to preferences");
checkScrolling(true);
var grid = gManagerWindow.document.getElementById("detail-grid");
var settings = grid.querySelectorAll("rows > setting");
is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
// Force bindings to apply
settings[0].clientTop;
ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
Services.prefs.setBoolPref("extensions.inlinesettings1.bool", false);
var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input");
@ -288,9 +303,6 @@ add_test(function() {
var settings = grid.querySelectorAll("rows > setting");
is(settings.length, 4, "Grid should have settings children");
// Force bindings to apply
settings[0].clientTop;
ok(settings[0].hasAttribute("first-row"), "First visible row should have first-row attribute");
Services.prefs.setBoolPref("extensions.inlinesettings3.radioBool", false);
var radios = settings[0].getElementsByTagName("radio");
@ -354,9 +366,6 @@ add_test(function() {
var settings = grid.querySelectorAll("rows > setting");
is(settings.length, 5, "Grid should have settings children");
// Force bindings to apply
settings[0].clientTop;
var node = settings[0];
node = settings[0];
is_element_hidden(node, "Unsupported settings should not be visible");
@ -439,10 +448,15 @@ add_test(function() {
var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
addon.parentNode.ensureElementIsVisible(addon);
var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
wait_for_view_load(gManagerWindow, function() {
is(gManagerWindow.gViewController.currentViewId,
"addons://detail/inlinesettings1%40tests.mozilla.org",
"Current view should not scroll to preferences");
checkScrolling(false);
var grid = gManagerWindow.document.getElementById("detail-grid");
var settings = grid.querySelectorAll("rows > setting");
is(settings.length, SETTINGS_ROWS, "Grid should have settings children");

View File

@ -66,7 +66,7 @@
min-height: 41px;
}
@media all and (max-width: 600px) {
@media (max-width: 600px) {
.global-warning-text {
display: none;
}
@ -206,7 +206,7 @@
}
/* Maximize the size of the viewport when the window is small */
@media all and (max-width: 800px) {
@media (max-width: 800px) {
.category-name {
display: none;
}

View File

@ -73,7 +73,7 @@
border-bottom: 1px solid rgba(50, 65, 92, 0.4);
}
@media all and (max-width: 600px) {
@media (max-width: 600px) {
.global-warning-text {
display: none;
}
@ -239,7 +239,7 @@
}
/* Maximize the size of the viewport when the window is small */
@media all and (max-width: 800px) {
@media (max-width: 800px) {
.category-name {
display: none;
}

View File

@ -61,7 +61,7 @@ html|html {
}
%ifdef WINSTRIPE_AERO
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
*|*:root {
color: #000;
background-color: #CCD9EA;
@ -72,7 +72,7 @@ html|html {
}
}
@media all and (-moz-windows-compositor) {
@media (-moz-windows-compositor) {
*|*:root {
color: #000;
/* Blame shorlander for this monstrosity. */
@ -117,7 +117,7 @@ html|html {
}
%ifdef WINSTRIPE_AERO
@media all and (-moz-windows-compositor) {
@media (-moz-windows-compositor) {
/* Buttons */
*|button,
menulist,

View File

@ -1,6 +1,6 @@
%include menu.css
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
menubar > menu:-moz-lwtheme {
-moz-appearance: menuitem;
}

View File

@ -69,7 +69,7 @@ menuitem[_moz-menuactive="true"][disabled="true"],
text-shadow: none;
}
@media all and (-moz-windows-classic) {
@media (-moz-windows-classic) {
menu[disabled="true"],
menubar > menu[disabled="true"][_moz-menuactive="true"],
menuitem[disabled="true"],

View File

@ -70,7 +70,7 @@ notification[type="critical"] .messageImage {
%ifdef XP_WIN
/*
XXX: apply styles to all themes until bug 509642 is fixed
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
*/
.popup-notification-menubutton[type="menu-button"] {
-moz-appearance: none;

View File

@ -131,7 +131,7 @@ panel[type="arrow"] {
%endif
%ifdef XP_WIN
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
panel[type="arrow"][side="top"],
panel[type="arrow"][side="bottom"] {
margin-left: -25px;

View File

@ -1,6 +1,6 @@
%include toolbarbutton.css
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
toolbarbutton:-moz-lwtheme:not([disabled="true"]) {
color: inherit;
text-shadow: inherit;

View File

@ -96,7 +96,7 @@ toolbarbutton[disabled="true"] {
text-shadow: none;
}
@media all and (-moz-windows-classic) {
@media (-moz-windows-classic) {
toolbarbutton[disabled="true"] {
color: ThreeDShadow;
text-shadow: 1px 1px ThreeDHighlight;
@ -113,7 +113,7 @@ toolbarbutton[checked="true"]:not([disabled="true"]) {
color: ButtonText;
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
toolbarbutton:-moz-lwtheme {
text-shadow: none;
}

View File

@ -1,6 +1,6 @@
%include downloads.css
@media all and (-moz-windows-compositor) {
@media (-moz-windows-compositor) {
#downloadManager {
-moz-appearance: -moz-win-glass;
background: transparent;

View File

@ -10,7 +10,7 @@
color: -moz-DialogText;
}
@media all and (-moz-windows-compositor) {
@media (-moz-windows-compositor) {
#genericAbout {
-moz-appearance: -moz-win-glass;
background: transparent;

View File

@ -87,7 +87,7 @@
border-bottom: 1px solid #CAD4E0;
}
@media all and (max-width: 600px) {
@media (max-width: 600px) {
.global-warning-text {
display: none;
}
@ -251,7 +251,7 @@
}
/* Maximize the size of the viewport when the window is small */
@media all and (max-width: 800px) {
@media (max-width: 800px) {
.category-name {
display: none;
}
@ -319,7 +319,7 @@
margin: 0;
}
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#header-search {
-moz-appearance: none;
border: 1px solid rgba(0, 0, 0, 0.32);

View File

@ -207,7 +207,7 @@
}
%ifdef WINSTRIPE_AERO
@media all and (-moz-windows-default-theme) {
@media (-moz-windows-default-theme) {
#footer {
background-color: #f1f5fb;
box-shadow: 0px 1px 2px rgb(204,214,234) inset;

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 et cindent: */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -59,9 +59,14 @@ public:
SharedLibrary& operator=(const SharedLibrary& aEntry)
{
// Gracefully handle self assignment
if (this == &aEntry) return *this;
mStart = aEntry.mStart;
mEnd = aEntry.mEnd;
mOffset = aEntry.mOffset;
if (mName)
free(mName);
mName = strdup(aEntry.mName);
return *this;
}

View File

@ -408,6 +408,10 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
mType = jenv->GetIntField(jobj, jTypeField);
switch (mType) {
case TILE_SIZE:
ReadP0Field(jenv);
break;
case SIZE_CHANGED:
ReadP0Field(jenv);
ReadP1Field(jenv);

View File

@ -532,6 +532,7 @@ public:
ACTIVITY_START = 17,
BROADCAST = 19,
VIEWPORT = 20,
TILE_SIZE = 21,
dummy_java_enum_list_end
};

View File

@ -87,6 +87,7 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
// The dimensions of the current android view
static gfxIntSize gAndroidBounds = gfxIntSize(0, 0);
static gfxIntSize gAndroidTileSize = gfxIntSize(0, 0);
static gfxIntSize gAndroidScreenBounds;
#ifdef ACCESSIBILITY
@ -981,6 +982,12 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
win->OnDraw(ae);
break;
case AndroidGeckoEvent::TILE_SIZE: {
gAndroidTileSize.width = ae->P0().x;
gAndroidTileSize.height = ae->P0().y;
break;
}
case AndroidGeckoEvent::IME_EVENT:
win->UserActivity();
if (win->mFocus) {
@ -1196,42 +1203,56 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
sDirectTexture->Reallocate(gAndroidBounds.width, gAndroidBounds.height);
}
sDirectTexture->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, &bits);
sDirectTexture->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, ae->Rect(), &bits);
} else {
bits = client.LockBufferBits();
}
if (!bits) {
ALOG("### Failed to lock buffer");
if (sHasDirectTexture) {
sDirectTexture->Unlock();
} else {
client.UnlockBuffer();
}
} else {
nsRefPtr<gfxImageSurface> targetSurface =
new gfxImageSurface(bits, gfxIntSize(gAndroidBounds.width, gAndroidBounds.height), gAndroidBounds.width * 2,
gfxASurface::ImageFormatRGB16_565);
if (targetSurface->CairoStatus()) {
ALOG("### Failed to create a valid surface from the bitmap");
} else {
if (sHasDirectTexture) {
// XXX: lock only the dirty rect above and pass it in here
DrawTo(targetSurface);
} else {
DrawTo(targetSurface, ae->Rect());
}
// If tile size is 0,0, we assume we only have a single tile
int tileWidth = (gAndroidTileSize.width > 0) ? gAndroidTileSize.width : gAndroidBounds.width;
int tileHeight = (gAndroidTileSize.height > 0) ? gAndroidTileSize.height : gAndroidBounds.height;
if (metadataProvider) {
metadataProvider->GetDrawMetadata(metadata);
bool drawSuccess = true;
int offset = 0;
for (int y = 0; y < gAndroidBounds.height; y += tileHeight) {
for (int x = 0; x < gAndroidBounds.width; x += tileWidth) {
int width = NS_MIN(tileWidth, gAndroidBounds.width - x);
int height = NS_MIN(tileHeight, gAndroidBounds.height - y);
nsRefPtr<gfxImageSurface> targetSurface =
new gfxImageSurface(bits + offset,
gfxIntSize(width, height),
width * 2,
gfxASurface::ImageFormatRGB16_565);
offset += width * height * 2;
if (targetSurface->CairoStatus()) {
ALOG("### Failed to create a valid surface from the bitmap");
drawSuccess = false;
break;
} else {
targetSurface->SetDeviceOffset(gfxPoint(-x, -y));
DrawTo(targetSurface, ae->Rect());
}
}
}
if (sHasDirectTexture) {
sDirectTexture->Unlock();
} else {
client.UnlockBuffer();
// Don't fill in the draw metadata on an unsuccessful draw
if (drawSuccess && metadataProvider) {
metadataProvider->GetDrawMetadata(metadata);
}
}
if (sHasDirectTexture) {
sDirectTexture->Unlock();
} else {
client.UnlockBuffer();
}
client.EndDrawing(ae->Rect(), metadata);
return;
#endif

View File

@ -191,12 +191,25 @@ nsWindow::Show(bool aState)
if (mWindowType == eWindowType_invisible)
return NS_OK;
if (mVisible == aState)
return NS_OK;
mVisible = aState;
if (!IS_TOPLEVEL())
return mParent ? mParent->Show(aState) : NS_OK;
if (aState)
if (aState) {
BringToTop();
} else {
for (unsigned int i = 0; i < sTopWindows.Length(); i++) {
nsWindow *win = sTopWindows[i];
if (!win->mVisible)
continue;
win->BringToTop();
break;
}
}
return NS_OK;
}
@ -210,8 +223,8 @@ nsWindow::IsVisible(bool & aState)
NS_IMETHODIMP
nsWindow::ConstrainPosition(bool aAllowSlop,
PRInt32 *aX,
PRInt32 *aY)
PRInt32 *aX,
PRInt32 *aY)
{
return NS_OK;
}
@ -287,10 +300,8 @@ nsWindow::Invalidate(const nsIntRect &aRect,
nsWindow *parent = mParent;
while (parent && parent != sTopWindows[0])
parent = parent->mParent;
if (parent != sTopWindows[0]) {
LOG(" parent isn't top window, bailing");
if (parent != sTopWindows[0])
return NS_OK;
}
mDirtyRegion.Or(mDirtyRegion, aRect);
gWindowToRedraw = this;
@ -350,13 +361,13 @@ NS_IMETHODIMP_(void)
nsWindow::SetInputContext(const InputContext& aContext,
const InputContextAction& aAction)
{
mInputContext = aContext;
mInputContext = aContext;
}
NS_IMETHODIMP_(InputContext)
nsWindow::GetInputContext()
{
return mInputContext;
return mInputContext;
}
NS_IMETHODIMP
@ -429,5 +440,6 @@ nsWindow::BringToTop()
nsGUIEvent event(true, NS_ACTIVATE, this);
(*mEventCallback)(&event);
Update();
}