import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,49 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['test']
XPIDL_SOURCES += [
'nsIDialogParamBlock.idl',
'nsIPromptFactory.idl',
'nsIPromptService.idl',
'nsIPromptService2.idl',
'nsIWindowWatcher.idl',
'nsPIPromptService.idl',
'nsPIWindowWatcher.idl',
]
XPIDL_MODULE = 'windowwatcher'
EXPORTS += [
'nsPromptUtils.h',
]
UNIFIED_SOURCES += [
'nsAutoWindowStateHelper.cpp',
'nsWindowWatcher.cpp',
]
EXPORTS += [
'nsWindowWatcher.h',
]
if CONFIG['MOZ_XUL']:
UNIFIED_SOURCES += [
'nsDialogParamBlock.cpp',
]
FINAL_LIBRARY = 'xul'
# For nsJSUtils
LOCAL_INCLUDES += [
'/docshell/base',
'/dom/base',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']
include('/ipc/chromium/chromium-config.mozbuild')

View file

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAutoWindowStateHelper.h"
#include "mozilla/dom/Event.h"
#include "nsIDocument.h"
#include "nsIDOMEvent.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsString.h"
using namespace mozilla;
using namespace mozilla::dom;
/****************************************************************
****************** nsAutoWindowStateHelper *********************
****************************************************************/
nsAutoWindowStateHelper::nsAutoWindowStateHelper(nsPIDOMWindowOuter* aWindow)
: mWindow(aWindow)
, mDefaultEnabled(DispatchEventToChrome("DOMWillOpenModalDialog"))
{
if (mWindow) {
mWindow->EnterModalState();
}
}
nsAutoWindowStateHelper::~nsAutoWindowStateHelper()
{
if (mWindow) {
mWindow->LeaveModalState();
}
if (mDefaultEnabled) {
DispatchEventToChrome("DOMModalDialogClosed");
}
}
bool
nsAutoWindowStateHelper::DispatchEventToChrome(const char* aEventName)
{
// XXXbz should we skip dispatching the event if the inner changed?
// That is, should we store both the inner and the outer?
if (!mWindow) {
return true;
}
// The functions of nsContentUtils do not provide the required behavior,
// so the following is inlined.
nsIDocument* doc = mWindow->GetExtantDoc();
if (!doc) {
return true;
}
ErrorResult rv;
RefPtr<Event> event = doc->CreateEvent(NS_LITERAL_STRING("Events"), rv);
if (rv.Failed()) {
rv.SuppressException();
return false;
}
event->InitEvent(NS_ConvertASCIItoUTF16(aEventName), true, true);
event->SetTrusted(true);
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
nsCOMPtr<EventTarget> target = do_QueryInterface(mWindow);
bool defaultActionEnabled;
target->DispatchEvent(event, &defaultActionEnabled);
return defaultActionEnabled;
}

View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __nsAutoWindowStateHelper_h
#define __nsAutoWindowStateHelper_h
#include "nsCOMPtr.h"
#include "nsPIDOMWindow.h"
/**
* Helper class for dealing with notifications around opening modal
* windows.
*/
class nsPIDOMWindowOuter;
class nsAutoWindowStateHelper
{
public:
explicit nsAutoWindowStateHelper(nsPIDOMWindowOuter* aWindow);
~nsAutoWindowStateHelper();
bool DefaultEnabled() { return mDefaultEnabled; }
protected:
bool DispatchEventToChrome(const char* aEventName);
nsCOMPtr<nsPIDOMWindowOuter> mWindow;
bool mDefaultEnabled;
};
#endif

View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDialogParamBlock.h"
#include "nsString.h"
#include "nsReadableUtils.h"
NS_IMPL_ISUPPORTS(nsDialogParamBlock, nsIDialogParamBlock)
nsDialogParamBlock::nsDialogParamBlock()
: mNumStrings(0)
, mString(nullptr)
{
for (int32_t i = 0; i < kNumInts; i++) {
mInt[i] = 0;
}
}
nsDialogParamBlock::~nsDialogParamBlock()
{
delete[] mString;
}
NS_IMETHODIMP
nsDialogParamBlock::SetNumberStrings(int32_t aNumStrings)
{
if (mString) {
return NS_ERROR_ALREADY_INITIALIZED;
}
mString = new nsString[aNumStrings];
if (!mString) {
return NS_ERROR_OUT_OF_MEMORY;
}
mNumStrings = aNumStrings;
return NS_OK;
}
NS_IMETHODIMP
nsDialogParamBlock::GetInt(int32_t aIndex, int32_t* aResult)
{
nsresult rv = InBounds(aIndex, kNumInts);
if (rv == NS_OK) {
*aResult = mInt[aIndex];
}
return rv;
}
NS_IMETHODIMP
nsDialogParamBlock::SetInt(int32_t aIndex, int32_t aInt)
{
nsresult rv = InBounds(aIndex, kNumInts);
if (rv == NS_OK) {
mInt[aIndex] = aInt;
}
return rv;
}
NS_IMETHODIMP
nsDialogParamBlock::GetString(int32_t aIndex, char16_t** aResult)
{
if (mNumStrings == 0) {
SetNumberStrings(kNumStrings);
}
nsresult rv = InBounds(aIndex, mNumStrings);
if (rv == NS_OK) {
*aResult = ToNewUnicode(mString[aIndex]);
}
return rv;
}
NS_IMETHODIMP
nsDialogParamBlock::SetString(int32_t aIndex, const char16_t* aString)
{
if (mNumStrings == 0) {
SetNumberStrings(kNumStrings);
}
nsresult rv = InBounds(aIndex, mNumStrings);
if (rv == NS_OK) {
mString[aIndex] = aString;
}
return rv;
}
NS_IMETHODIMP
nsDialogParamBlock::GetObjects(nsIMutableArray** aObjects)
{
NS_ENSURE_ARG_POINTER(aObjects);
NS_IF_ADDREF(*aObjects = mObjects);
return NS_OK;
}
NS_IMETHODIMP
nsDialogParamBlock::SetObjects(nsIMutableArray* aObjects)
{
mObjects = aObjects;
return NS_OK;
}

View file

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __nsDialogParamBlock_h
#define __nsDialogParamBlock_h
#include "nsIDialogParamBlock.h"
#include "nsIMutableArray.h"
#include "nsCOMPtr.h"
// {4E4AAE11-8901-46cc-8217-DAD7C5415873}
#define NS_DIALOGPARAMBLOCK_CID \
{0x4e4aae11, 0x8901, 0x46cc, {0x82, 0x17, 0xda, 0xd7, 0xc5, 0x41, 0x58, 0x73}}
class nsString;
class nsDialogParamBlock : public nsIDialogParamBlock
{
public:
nsDialogParamBlock();
NS_DECL_NSIDIALOGPARAMBLOCK
NS_DECL_ISUPPORTS
protected:
virtual ~nsDialogParamBlock();
private:
enum { kNumInts = 8, kNumStrings = 16 };
nsresult InBounds(int32_t aIndex, int32_t aMax)
{
return aIndex >= 0 && aIndex < aMax ? NS_OK : NS_ERROR_ILLEGAL_VALUE;
}
int32_t mInt[kNumInts];
int32_t mNumStrings;
nsString* mString;
nsCOMPtr<nsIMutableArray> mObjects;
};
#endif

View file

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIMutableArray;
/**
* An interface to pass strings, integers and nsISupports to a dialog
*/
[scriptable, uuid(f76c0901-437a-11d3-b7a0-e35db351b4bc)]
interface nsIDialogParamBlock: nsISupports {
/** Get or set an integer to pass.
* Index must be in the range 0..7
*/
int32_t GetInt( in int32_t inIndex );
void SetInt( in int32_t inIndex, in int32_t inInt );
/** Set the maximum number of strings to pass. Default is 16.
* Use before setting any string (If you want to change it from the default).
*/
void SetNumberStrings( in int32_t inNumStrings );
/** Get or set an string to pass.
* Index starts at 0
*/
wstring GetString( in int32_t inIndex );
void SetString( in int32_t inIndex, in wstring inString);
/**
* A place where you can store an nsIMutableArray to pass nsISupports
*/
attribute nsIMutableArray objects;
};
%{C++
#define NS_DIALOGPARAMBLOCK_CONTRACTID "@mozilla.org/embedcomp/dialogparam;1"
%}

View file

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
/**
* This interface allows creating various prompts that have a specific parent.
*/
[scriptable, uuid(2803541c-c96a-4ff1-bd7c-9cb566d46aeb)]
interface nsIPromptFactory : nsISupports
{
/**
* Returns an object implementing the specified interface that creates
* prompts parented to aParent.
*/
void getPrompt(in mozIDOMWindowProxy aParent, in nsIIDRef iid,
[iid_is(iid),retval] out nsQIResult result);
};

View file

@ -0,0 +1,346 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
/**
* This is the interface to the embeddable prompt service; the service that
* implements nsIPrompt. Its interface is designed to be just nsIPrompt, each
* method modified to take a parent window parameter.
*
* Accesskeys can be attached to buttons and checkboxes by inserting an &
* before the accesskey character in the checkbox message or button title. For
* a real &, use && instead. (A "button title" generally refers to the text
* label of a button.)
*
* One note: in all cases, the parent window parameter can be null. However,
* these windows are all intended to have parents. So when no parent is
* specified, the implementation should try hard to find a suitable foster
* parent.
*
* Implementations are free to choose how they present the various button
* types. For example, while prompts that give the user a choice between OK
* and Cancel are required to return a boolean value indicating whether or not
* the user accepted the prompt (pressed OK) or rejected the prompt (pressed
* Cancel), the implementation of this interface could very well speak the
* prompt to the user instead of rendering any visual user-interface. The
* standard button types are merely idioms used to convey the nature of the
* choice the user is to make.
*
* Because implementations of this interface may loosely interpret the various
* button types, it is advised that text messages passed to these prompts do
* not refer to the button types by name. For example, it is inadvisable to
* tell the user to "Press OK to proceed." Instead, such a prompt might be
* rewritten to ask the user: "Would you like to proceed?"
*/
[scriptable, uuid(404ebfa2-d8f4-4c94-8416-e65a55f9df5a)]
interface nsIPromptService : nsISupports
{
/**
* Puts up an alert dialog with an OK button.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
*/
void alert(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText);
/**
* Puts up an alert dialog with an OK button and a labeled checkbox.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aCheckMsg
* Text to appear with the checkbox.
* @param aCheckState
* Contains the initial checked state of the checkbox when this method
* is called and the final checked state after this method returns.
*/
void alertCheck(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
in wstring aCheckMsg,
inout boolean aCheckState);
/**
* Puts up a dialog with OK and Cancel buttons.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
*
* @return true for OK, false for Cancel
*/
boolean confirm(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText);
/**
* Puts up a dialog with OK and Cancel buttons and a labeled checkbox.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aCheckMsg
* Text to appear with the checkbox.
* @param aCheckState
* Contains the initial checked state of the checkbox when this method
* is called and the final checked state after this method returns.
*
* @return true for OK, false for Cancel
*/
boolean confirmCheck(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
in wstring aCheckMsg,
inout boolean aCheckState);
/**
* Button Flags
*
* The following flags are combined to form the aButtonFlags parameter passed
* to confirmEx. See confirmEx for more information on how the flags may be
* combined.
*/
/**
* Button Position Flags
*/
const unsigned long BUTTON_POS_0 = 1;
const unsigned long BUTTON_POS_1 = 1 << 8;
const unsigned long BUTTON_POS_2 = 1 << 16;
/**
* Button Title Flags (used to set the labels of buttons in the prompt)
*/
const unsigned long BUTTON_TITLE_OK = 1;
const unsigned long BUTTON_TITLE_CANCEL = 2;
const unsigned long BUTTON_TITLE_YES = 3;
const unsigned long BUTTON_TITLE_NO = 4;
const unsigned long BUTTON_TITLE_SAVE = 5;
const unsigned long BUTTON_TITLE_DONT_SAVE = 6;
const unsigned long BUTTON_TITLE_REVERT = 7;
const unsigned long BUTTON_TITLE_IS_STRING = 127;
/**
* Button Default Flags (used to select which button is the default one)
*/
const unsigned long BUTTON_POS_0_DEFAULT = 0;
const unsigned long BUTTON_POS_1_DEFAULT = 1 << 24;
const unsigned long BUTTON_POS_2_DEFAULT = 1 << 25;
/**
* Causes the buttons to be initially disabled. They are enabled after a
* timeout expires. The implementation may interpret this loosely as the
* intent is to ensure that the user does not click through a security dialog
* too quickly. Strictly speaking, the implementation could choose to ignore
* this flag.
*/
const unsigned long BUTTON_DELAY_ENABLE = 1 << 26;
/**
* Selects the standard set of OK/Cancel buttons.
*/
const unsigned long STD_OK_CANCEL_BUTTONS = (BUTTON_TITLE_OK * BUTTON_POS_0) +
(BUTTON_TITLE_CANCEL * BUTTON_POS_1);
/**
* Selects the standard set of Yes/No buttons.
*/
const unsigned long STD_YES_NO_BUTTONS = (BUTTON_TITLE_YES * BUTTON_POS_0) +
(BUTTON_TITLE_NO * BUTTON_POS_1);
/**
* Puts up a dialog with up to 3 buttons and an optional, labeled checkbox.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aButtonFlags
* A combination of Button Flags.
* @param aButton0Title
* Used when button 0 uses TITLE_IS_STRING
* @param aButton1Title
* Used when button 1 uses TITLE_IS_STRING
* @param aButton2Title
* Used when button 2 uses TITLE_IS_STRING
* @param aCheckMsg
* Text to appear with the checkbox. Null if no checkbox.
* @param aCheckState
* Contains the initial checked state of the checkbox when this method
* is called and the final checked state after this method returns.
*
* @return index of the button pressed.
*
* Buttons are numbered 0 - 2. The implementation can decide whether the
* sequence goes from right to left or left to right. Button 0 is the
* default button unless one of the Button Default Flags is specified.
*
* A button may use a predefined title, specified by one of the Button Title
* Flags values. Each title value can be multiplied by a position value to
* assign the title to a particular button. If BUTTON_TITLE_IS_STRING is
* used for a button, the string parameter for that button will be used. If
* the value for a button position is zero, the button will not be shown.
*
* In general, aButtonFlags is constructed per the following example:
*
* aButtonFlags = (BUTTON_POS_0) * (BUTTON_TITLE_AAA) +
* (BUTTON_POS_1) * (BUTTON_TITLE_BBB) +
* BUTTON_POS_1_DEFAULT;
*
* where "AAA" and "BBB" correspond to one of the button titles.
*/
int32_t confirmEx(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
in unsigned long aButtonFlags,
in wstring aButton0Title,
in wstring aButton1Title,
in wstring aButton2Title,
in wstring aCheckMsg,
inout boolean aCheckState);
/**
* Puts up a dialog with an edit field and an optional, labeled checkbox.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aValue
* Contains the default value for the dialog field when this method
* is called (null value is ok). Upon return, if the user pressed
* OK, then this parameter contains a newly allocated string value.
* Otherwise, the parameter's value is unmodified.
* @param aCheckMsg
* Text to appear with the checkbox. If null, check box will not be shown.
* @param aCheckState
* Contains the initial checked state of the checkbox when this method
* is called and the final checked state after this method returns.
*
* @return true for OK, false for Cancel.
*/
boolean prompt(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
inout wstring aValue,
in wstring aCheckMsg,
inout boolean aCheckState);
/**
* Puts up a dialog with an edit field, a password field, and an optional,
* labeled checkbox.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aUsername
* Contains the default value for the username field when this method
* is called (null value is ok). Upon return, if the user pressed OK,
* then this parameter contains a newly allocated string value.
* Otherwise, the parameter's value is unmodified.
* @param aPassword
* Contains the default value for the password field when this method
* is called (null value is ok). Upon return, if the user pressed OK,
* then this parameter contains a newly allocated string value.
* Otherwise, the parameter's value is unmodified.
* @param aCheckMsg
* Text to appear with the checkbox. If null, check box will not be shown.
* @param aCheckState
* Contains the initial checked state of the checkbox when this method
* is called and the final checked state after this method returns.
*
* @return true for OK, false for Cancel.
*/
boolean promptUsernameAndPassword(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
inout wstring aUsername,
inout wstring aPassword,
in wstring aCheckMsg,
inout boolean aCheckState);
/**
* Puts up a dialog with a password field and an optional, labeled checkbox.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aPassword
* Contains the default value for the password field when this method
* is called (null value is ok). Upon return, if the user pressed OK,
* then this parameter contains a newly allocated string value.
* Otherwise, the parameter's value is unmodified.
* @param aCheckMsg
* Text to appear with the checkbox. If null, check box will not be shown.
* @param aCheckState
* Contains the initial checked state of the checkbox when this method
* is called and the final checked state after this method returns.
*
* @return true for OK, false for Cancel.
*/
boolean promptPassword(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
inout wstring aPassword,
in wstring aCheckMsg,
inout boolean aCheckState);
/**
* Puts up a dialog box which has a list box of strings from which the user
* may make a single selection.
*
* @param aParent
* The parent window or null.
* @param aDialogTitle
* Text to appear in the title of the dialog.
* @param aText
* Text to appear in the body of the dialog.
* @param aCount
* The length of the aSelectList array parameter.
* @param aSelectList
* The list of strings to display.
* @param aOutSelection
* Contains the index of the selected item in the list when this
* method returns true.
*
* @return true for OK, false for Cancel.
*/
boolean select(in mozIDOMWindowProxy aParent,
in wstring aDialogTitle,
in wstring aText,
in uint32_t aCount,
[array, size_is(aCount)] in wstring aSelectList,
out long aOutSelection);
};

View file

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIPromptService.idl"
interface nsIAuthInformation;
interface nsIAuthPromptCallback;
interface nsICancelable;
interface nsIChannel;
interface mozIDOMWindowProxy;
/**
* This is an improved version of nsIPromptService that is less prescriptive
* about the resulting user interface.
*
* @status INCOMPLETE do not freeze before fixing bug 228207
*/
[scriptable, uuid(3775ad32-8326-422b-9ff3-87ef1d3f9f0e)]
interface nsIPromptService2 : nsIPromptService {
// NOTE: These functions differ from their nsIAuthPrompt counterparts by
// having additional checkbox parameters
// checkValue can be null meaning to show no checkbox
// checkboxLabel is a wstring so that it can be null from both JS and C++ in
// a convenient way
//
// See nsIAuthPrompt2 for documentation on the semantics of the other
// parameters.
boolean promptAuth(in mozIDOMWindowProxy aParent,
in nsIChannel aChannel,
in uint32_t level,
in nsIAuthInformation authInfo,
in wstring checkboxLabel,
inout boolean checkValue);
nsICancelable asyncPromptAuth(in mozIDOMWindowProxy aParent,
in nsIChannel aChannel,
in nsIAuthPromptCallback aCallback,
in nsISupports aContext,
in uint32_t level,
in nsIAuthInformation authInfo,
in wstring checkboxLabel,
inout boolean checkValue);
};

View file

@ -0,0 +1,167 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
interface nsIObserver;
interface nsIPrompt;
interface nsIAuthPrompt;
interface nsISimpleEnumerator;
interface nsIWebBrowserChrome;
interface nsIWindowCreator;
/**
* nsIWindowWatcher is the keeper of Gecko/DOM Windows. It maintains
* a list of open top-level windows, and allows some operations on them.
* Usage notes:
* This component has an |activeWindow| property. Clients may expect
* this property to be always current, so to properly integrate this component
* the application will need to keep it current by setting the property
* as the active window changes.
* This component should not keep a (XPCOM) reference to any windows;
* the implementation will claim no ownership. Windows must notify
* this component when they are created or destroyed, so only a weak
* reference is kept. Note that there is no interface for such notifications
* (not a public one, anyway). This is taken care of both in Mozilla and
* by common embedding code. Embedding clients need do nothing special
* about that requirement.
* This component must be initialized at application startup by calling
* setWindowCreator.
*/
[scriptable, uuid(641fe945-6902-4b3f-87c2-0daef32499b3)]
interface nsIWindowWatcher : nsISupports {
/** Create a new window. It will automatically be added to our list
(via addWindow()).
@param aParent parent window, if any. Null if no parent. If it is
impossible to get to an nsIWebBrowserChrome from aParent, this
method will effectively act as if aParent were null.
@param aURL url to which to open the new window. Must already be
escaped, if applicable. can be null.
@param aName window name from JS window.open. can be null. If a window
with this name already exists, the openWindow call may just load
aUrl in it (if aUrl is not null) and return it.
@param aFeatures window features from JS window.open. can be null.
@param aArguments extra argument(s) to the new window, to be attached
as the |arguments| property. An nsIArray will be
unwound into multiple arguments (but not recursively!).
can be null.
@return the new window
@note This method may examine the JS context stack for purposes of
determining the security context to use for the search for a given
window named aName.
@note This method should try to set the default charset for the new
window to the default charset of aParent. This is not guaranteed,
however.
@note This method may dispatch a "toplevel-window-ready" notification
via nsIObserverService if the window did not already exist.
*/
mozIDOMWindowProxy openWindow(in mozIDOMWindowProxy aParent, in string aUrl,
in string aName, in string aFeatures,
in nsISupports aArguments);
/** Clients of this service can register themselves to be notified
when a window is opened or closed (added to or removed from this
service). This method adds an aObserver to the list of objects
to be notified.
@param aObserver the object to be notified when windows are
opened or closed. Its Observe method will be
called with the following parameters:
aObserver::Observe interprets its parameters so:
aSubject the window being opened or closed, sent as an nsISupports
which can be QIed to an nsIDOMWindow.
aTopic a wstring, either "domwindowopened" or "domwindowclosed".
someData not used.
*/
void registerNotification(in nsIObserver aObserver);
/** Clients of this service can register themselves to be notified
when a window is opened or closed (added to or removed from this
service). This method removes an aObserver from the list of objects
to be notified.
@param aObserver the observer to be removed.
*/
void unregisterNotification(in nsIObserver aObserver);
/** Get an iterator for currently open windows in the order they were opened,
guaranteeing that each will be visited exactly once.
@return an enumerator which will itself return nsISupports objects which
can be QIed to an nsIDOMWindow
*/
nsISimpleEnumerator getWindowEnumerator();
/** Return a newly created nsIPrompt implementation.
@param aParent the parent window used for posing alerts. can be null.
@return a new nsIPrompt object
*/
nsIPrompt getNewPrompter(in mozIDOMWindowProxy aParent);
/** Return a newly created nsIAuthPrompt implementation.
@param aParent the parent window used for posing alerts. can be null.
@return a new nsIAuthPrompt object
*/
nsIAuthPrompt getNewAuthPrompter(in mozIDOMWindowProxy aParent);
/** Set the window creator callback. It must be filled in by the app.
openWindow will use it to create new windows.
@param creator the callback. if null, the callback will be cleared
and window creation capabilities lost.
*/
void setWindowCreator(in nsIWindowCreator creator);
/** Returns true if a window creator callback has been set, false otherwise.
*/
boolean hasWindowCreator();
/** Retrieve the chrome window mapped to the given DOM window. Window
Watcher keeps a list of all top-level DOM windows currently open,
along with their corresponding chrome interfaces. Since DOM Windows
lack a (public) means of retrieving their corresponding chrome,
this method will do that.
@param aWindow the DOM window whose chrome window the caller needs
@return the corresponding chrome window
*/
nsIWebBrowserChrome getChromeForWindow(in mozIDOMWindowProxy aWindow);
/**
Retrieve an existing window (or frame).
@param aTargetName the window name
@param aCurrentWindow a starting point in the window hierarchy to
begin the search. If null, each toplevel window
will be searched.
Note: This method will search all open windows for any window or
frame with the given window name. Make sure you understand the
security implications of this before using this method!
*/
mozIDOMWindowProxy getWindowByName(in AString aTargetName,
in mozIDOMWindowProxy aCurrentWindow);
/** The Watcher serves as a global storage facility for the current active
(frontmost non-floating-palette-type) window, storing and returning
it on demand. Users must keep this attribute current, including after
the topmost window is closed. This attribute obviously can return null
if no windows are open, but should otherwise always return a valid
window.
*/
attribute mozIDOMWindowProxy activeWindow;
};
%{C++
#define NS_WINDOWWATCHER_CONTRACTID "@mozilla.org/embedcomp/window-watcher;1"
%}

View file

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* The general dialog posing function within nsPromptService, for
private consumption, only. */
#include "nsISupports.idl"
interface nsIDOMWindow;
interface nsIDialogParamBlock;
[uuid(C60A1955-6CB3-4827-8EF8-4F5C668AF0B3)]
interface nsPIPromptService : nsISupports
{
%{C++
// eOpeningSound is obsolete but we need to support it for the compatibility.
// The implementers should use eSoundEventId instead.
enum {eMsg=0, eCheckboxMsg=1, eIconClass=2, eTitleMessage=3, eEditfield1Msg=4,
eEditfield2Msg=5, eEditfield1Value=6, eEditfield2Value=7,
eButton0Text=8, eButton1Text=9, eButton2Text=10, eButton3Text=11,
eDialogTitle=12, eOpeningSound=13};
enum {eButtonPressed=0, eCheckboxState=1, eNumberButtons=2,
eNumberEditfields=3, eEditField1Password=4, eDefaultButton=5,
eDelayButtonEnable=6, eSoundEventId=7};
%}
void doDialog(in nsIDOMWindow aParent, in nsIDialogParamBlock aParamBlock, in string aChromeURL);
};

View file

@ -0,0 +1,146 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Private "control" methods on the Window Watcher. These are annoying
bookkeeping methods, not part of the public (embedding) interface.
*/
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
interface nsIDOMWindow;
interface nsISimpleEnumerator;
interface nsIWebBrowserChrome;
interface nsIDocShellTreeItem;
interface nsIArray;
interface nsITabParent;
interface nsIDocShellLoadInfo;
[uuid(d162f9c4-19d5-4723-931f-f1e51bfa9f68)]
interface nsPIWindowWatcher : nsISupports
{
/** A window has been created. Add it to our list.
@param aWindow the window to add
@param aChrome the corresponding chrome window. The DOM window
and chrome will be mapped together, and the corresponding
chrome can be retrieved using the (not private)
method getChromeForWindow. If null, any extant mapping
will be cleared.
*/
void addWindow(in mozIDOMWindowProxy aWindow,
in nsIWebBrowserChrome aChrome);
/** A window has been closed. Remove it from our list.
@param aWindow the window to remove
*/
void removeWindow(in mozIDOMWindowProxy aWindow);
/** Like the public interface's open(), but can handle openDialog-style
arguments and calls which shouldn't result in us navigating the window.
@param aParent parent window, if any. Null if no parent. If it is
impossible to get to an nsIWebBrowserChrome from aParent, this
method will effectively act as if aParent were null.
@param aURL url to which to open the new window. Must already be
escaped, if applicable. can be null.
@param aName window name from JS window.open. can be null. If a window
with this name already exists, the openWindow call may just load
aUrl in it (if aUrl is not null) and return it.
@param aFeatures window features from JS window.open. can be null.
@param aCalledFromScript true if we were called from script.
@param aDialog use dialog defaults (see nsIDOMWindow::openDialog)
@param aNavigate true if we should navigate the new window to the
specified URL.
@param aArgs Window argument
@param aIsPopupSpam true if the window is a popup spam window; used for
popup blocker internals.
@param aForceNoOpener If true, force noopener behavior. This means not
looking for existing windows with the given name,
not setting an opener on the newly opened window,
and returning null from this method.
@param aLoadInfo if aNavigate is true, this allows the caller to pass in
an nsIDocShellLoadInfo to use for the navigation.
Callers can pass in null if they want the windowwatcher
to just construct a loadinfo itself. If aNavigate is
false, this argument is ignored.
@return the new window
@note This method may examine the JS context stack for purposes of
determining the security context to use for the search for a given
window named aName.
@note This method should try to set the default charset for the new
window to the default charset of the document in the calling window
(which is determined based on the JS stack and the value of
aParent). This is not guaranteed, however.
*/
mozIDOMWindowProxy openWindow2(in mozIDOMWindowProxy aParent, in string aUrl,
in string aName, in string aFeatures,
in boolean aCalledFromScript,
in boolean aDialog,
in boolean aNavigate,
in nsISupports aArgs,
in boolean aIsPopupSpam,
in boolean aForceNoOpener,
in nsIDocShellLoadInfo aLoadInfo);
/**
* Opens a new window using the most recent non-private browser
* window as its parent.
*
* @return the nsITabParent of the initial browser for the newly opened
* window.
*/
nsITabParent openWindowWithoutParent();
/**
* Opens a new window so that the window that aOpeningTab belongs to
* is set as the parent window. The newly opened window will also
* inherit load context information from aOpeningTab.
*
* @param aOpeningTab
* The nsITabParent that is requesting the new window be opened.
* @param aFeatures
* Window features if called with window.open or similar.
* @param aCalledFromJS
* True if called via window.open or similar.
* @param aOpenerFullZoom
* The current zoom multiplier for the opener tab. This is then
* applied to the newly opened window.
*
* @return the nsITabParent of the initial browser for the newly opened
* window.
*/
nsITabParent openWindowWithTabParent(in nsITabParent aOpeningTab,
in ACString aFeatures,
in boolean aCalledFromJS,
in float aOpenerFullZoom);
/**
* Find a named docshell tree item amongst all windows registered
* with the window watcher. This may be a subframe in some window,
* for example.
*
* @param aName the name of the window. Must not be null.
* @param aRequestor the tree item immediately making the request.
* We should make sure to not recurse down into its findItemWithName
* method.
* @param aOriginalRequestor the original treeitem that made the request.
* Used for security checks.
* @return the tree item with aName as the name, or null if there
* isn't one. "Special" names, like _self, _top, etc, will be
* treated specially only if aRequestor is null; in that case they
* will be resolved relative to the first window the windowwatcher
* knows about.
* @see findItemWithName methods on nsIDocShellTreeItem and
* nsIDocShellTreeOwner
*/
nsIDocShellTreeItem findItemWithName(in AString aName,
in nsIDocShellTreeItem aRequestor,
in nsIDocShellTreeItem aOriginalRequestor);
};

View file

@ -0,0 +1,141 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NSPROMPTUTILS_H_
#define NSPROMPTUTILS_H_
#include "nsIHttpChannel.h"
/**
* @file
* This file defines some helper functions that simplify interaction
* with authentication prompts.
*/
/**
* Given a username (possibly in DOMAIN\user form) and password, parses the
* domain out of the username if necessary and sets domain, username and
* password on the auth information object.
*/
inline void
NS_SetAuthInfo(nsIAuthInformation* aAuthInfo, const nsString& aUser,
const nsString& aPassword)
{
uint32_t flags;
aAuthInfo->GetFlags(&flags);
if (flags & nsIAuthInformation::NEED_DOMAIN) {
// Domain is separated from username by a backslash
int32_t idx = aUser.FindChar(char16_t('\\'));
if (idx == kNotFound) {
aAuthInfo->SetUsername(aUser);
} else {
aAuthInfo->SetDomain(Substring(aUser, 0, idx));
aAuthInfo->SetUsername(Substring(aUser, idx + 1));
}
} else {
aAuthInfo->SetUsername(aUser);
}
aAuthInfo->SetPassword(aPassword);
}
/**
* Gets the host and port from a channel and authentication info. This is the
* "logical" host and port for this authentication, i.e. for a proxy
* authentication it refers to the proxy, while for a host authentication it
* is the actual host.
*
* @param machineProcessing
* When this parameter is true, the host will be returned in ASCII
* (instead of UTF-8; this is relevant when IDN is used). In addition,
* the port will be returned as the real port even when it was not
* explicitly specified (when false, the port will be returned as -1 in
* this case)
*/
inline void
NS_GetAuthHostPort(nsIChannel* aChannel, nsIAuthInformation* aAuthInfo,
bool aMachineProcessing, nsCString& aHost, int32_t* aPort)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv)) {
return;
}
// Have to distinguish proxy auth and host auth here...
uint32_t flags;
aAuthInfo->GetFlags(&flags);
if (flags & nsIAuthInformation::AUTH_PROXY) {
nsCOMPtr<nsIProxiedChannel> proxied(do_QueryInterface(aChannel));
NS_ASSERTION(proxied, "proxy auth needs nsIProxiedChannel");
nsCOMPtr<nsIProxyInfo> info;
proxied->GetProxyInfo(getter_AddRefs(info));
NS_ASSERTION(info, "proxy auth needs nsIProxyInfo");
nsAutoCString idnhost;
info->GetHost(idnhost);
info->GetPort(aPort);
if (aMachineProcessing) {
nsCOMPtr<nsIIDNService> idnService =
do_GetService(NS_IDNSERVICE_CONTRACTID);
if (idnService) {
idnService->ConvertUTF8toACE(idnhost, aHost);
} else {
// Not much we can do here...
aHost = idnhost;
}
} else {
aHost = idnhost;
}
} else {
if (aMachineProcessing) {
uri->GetAsciiHost(aHost);
*aPort = NS_GetRealPort(uri);
} else {
uri->GetHost(aHost);
uri->GetPort(aPort);
}
}
}
/**
* Creates the key for looking up passwords in the password manager. This
* function uses the same format that Gecko functions have always used, thus
* ensuring backwards compatibility.
*/
inline void
NS_GetAuthKey(nsIChannel* aChannel, nsIAuthInformation* aAuthInfo,
nsCString& aKey)
{
// HTTP does this differently from other protocols
nsCOMPtr<nsIHttpChannel> http(do_QueryInterface(aChannel));
if (!http) {
nsCOMPtr<nsIURI> uri;
aChannel->GetURI(getter_AddRefs(uri));
uri->GetPrePath(aKey);
return;
}
// NOTE: For backwards-compatibility reasons, this must be the ASCII host.
nsCString host;
int32_t port = -1;
NS_GetAuthHostPort(aChannel, aAuthInfo, true, host, &port);
nsAutoString realm;
aAuthInfo->GetRealm(realm);
// Now assemble the key: host:port (realm)
aKey.Append(host);
aKey.Append(':');
aKey.AppendInt(port);
aKey.AppendLiteral(" (");
AppendUTF16toUTF8(realm, aKey);
aKey.Append(')');
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,156 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __nsWindowWatcher_h__
#define __nsWindowWatcher_h__
// {a21bfa01-f349-4394-a84c-8de5cf0737d0}
#define NS_WINDOWWATCHER_CID \
{0xa21bfa01, 0xf349, 0x4394, {0xa8, 0x4c, 0x8d, 0xe5, 0xcf, 0x7, 0x37, 0xd0}}
#include "nsCOMPtr.h"
#include "mozilla/Mutex.h"
#include "mozilla/Maybe.h"
#include "nsIWindowCreator.h" // for stupid compilers
#include "nsIWindowWatcher.h"
#include "nsIPromptFactory.h"
#include "nsITabParent.h"
#include "nsPIWindowWatcher.h"
#include "nsTArray.h"
class nsIURI;
class nsIDocShellTreeItem;
class nsIDocShellTreeOwner;
class nsPIDOMWindowOuter;
class nsWatcherWindowEnumerator;
class nsPromptService;
struct nsWatcherWindowEntry;
struct SizeSpec;
class nsWindowWatcher
: public nsIWindowWatcher
, public nsPIWindowWatcher
, public nsIPromptFactory
{
friend class nsWatcherWindowEnumerator;
public:
nsWindowWatcher();
nsresult Init();
NS_DECL_ISUPPORTS
NS_DECL_NSIWINDOWWATCHER
NS_DECL_NSPIWINDOWWATCHER
NS_DECL_NSIPROMPTFACTORY
static int32_t GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified);
protected:
virtual ~nsWindowWatcher();
friend class nsPromptService;
bool AddEnumerator(nsWatcherWindowEnumerator* aEnumerator);
bool RemoveEnumerator(nsWatcherWindowEnumerator* aEnumerator);
nsWatcherWindowEntry* FindWindowEntry(mozIDOMWindowProxy* aWindow);
nsresult RemoveWindow(nsWatcherWindowEntry* aInfo);
// Get the caller tree item. Look on the JS stack, then fall back
// to the parent if there's nothing there.
already_AddRefed<nsIDocShellTreeItem> GetCallerTreeItem(
nsIDocShellTreeItem* aParentItem);
// Unlike GetWindowByName this will look for a caller on the JS
// stack, and then fall back on aCurrentWindow if it can't find one.
// It also knows to not look for things if aForceNoOpener is set.
nsPIDOMWindowOuter* SafeGetWindowByName(const nsAString& aName,
bool aForceNoOpener,
mozIDOMWindowProxy* aCurrentWindow);
// Just like OpenWindowJS, but knows whether it got called via OpenWindowJS
// (which means called from script) or called via OpenWindow.
nsresult OpenWindowInternal(mozIDOMWindowProxy* aParent,
const char* aUrl,
const char* aName,
const char* aFeatures,
bool aCalledFromJS,
bool aDialog,
bool aNavigate,
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDocShellLoadInfo* aLoadInfo,
mozIDOMWindowProxy** aResult);
static nsresult URIfromURL(const char* aURL,
mozIDOMWindowProxy* aParent,
nsIURI** aURI);
static uint32_t CalculateChromeFlagsForChild(const nsACString& aFeaturesStr);
static uint32_t CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent,
const nsACString& aFeaturesStr,
bool aDialog,
bool aChromeURL,
bool aHasChromeParent,
bool aCalledFromJS);
static int32_t WinHasOption(const nsACString& aOptions, const char* aName,
int32_t aDefault, bool* aPresenceFlag);
/* Compute the right SizeSpec based on aFeatures */
static void CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult);
static nsresult ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
nsPIDOMWindowOuter* aParent,
bool aWindowIsNew,
bool aForceNoOpener,
mozIDOMWindowProxy** aOpenedWindow);
static void SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
mozIDOMWindowProxy* aParent,
bool aIsCallerChrome,
const SizeSpec& aSizeSpec,
mozilla::Maybe<float> aOpenerFullZoom =
mozilla::Nothing());
static void GetWindowTreeItem(mozIDOMWindowProxy* aWindow,
nsIDocShellTreeItem** aResult);
static void GetWindowTreeOwner(nsPIDOMWindowOuter* aWindow,
nsIDocShellTreeOwner** aResult);
private:
nsresult CreateChromeWindow(const nsACString& aFeatures,
nsIWebBrowserChrome* aParentChrome,
uint32_t aChromeFlags,
uint32_t aContextFlags,
nsITabParent* aOpeningTabParent,
mozIDOMWindowProxy* aOpener,
nsIWebBrowserChrome** aResult);
void MaybeDisablePersistence(const nsACString& aFeatures,
nsIDocShellTreeOwner* aTreeOwner);
static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags,
const nsACString& aFeatures,
bool &presenceFlag,
bool aDialog = false,
bool aHasChromeParent = false,
bool aChromeURL = false);
static uint32_t EnsureFlagsSafeForContent(uint32_t aChromeFlags,
bool aChromeURL = false);
protected:
nsTArray<nsWatcherWindowEnumerator*> mEnumeratorList;
nsWatcherWindowEntry* mOldestWindow;
mozilla::Mutex mListLock;
nsCOMPtr<nsIWindowCreator> mWindowCreator;
};
#endif

View file

@ -0,0 +1,9 @@
[DEFAULT]
tags = openwindow
[browser_new_content_window_chromeflags.js]
[browser_new_remote_window_flags.js]
run-if = e10s
[browser_new_content_window_from_chrome_principal.js]
[browser_new_sized_window.js]
skip-if = os == 'win' # Bug 1276802 - Opening windows from content on Windows might not get the size right

View file

@ -0,0 +1,278 @@
/**
* Tests that chromeFlags are set properly on windows that are
* being opened from content.
*/
// The following features set chrome flags on new windows and are
// supported by web content. The schema for each property on this
// object is as follows:
//
// <feature string>: {
// flag: <associated nsIWebBrowserChrome flag>,
// defaults_to: <what this feature defaults to normally>
// }
const ALLOWED = {
"toolbar": {
flag: Ci.nsIWebBrowserChrome.CHROME_TOOLBAR,
defaults_to: true,
},
"personalbar": {
flag: Ci.nsIWebBrowserChrome.CHROME_PERSONAL_TOOLBAR,
defaults_to: true,
},
"menubar": {
flag: Ci.nsIWebBrowserChrome.CHROME_MENUBAR,
defaults_to: true,
},
"scrollbars": {
flag: Ci.nsIWebBrowserChrome.CHROME_SCROLLBARS,
defaults_to: false,
},
"minimizable": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_MIN,
defaults_to: true,
},
};
// Construct a features string that flips all ALLOWED features
// to not be their defaults.
const ALLOWED_STRING = Object.keys(ALLOWED).map(feature => {
let toValue = ALLOWED[feature].defaults_to ? "no" : "yes";
return `${feature}=${toValue}`;
}).join(",");
// The following are not allowed from web content, at least
// in the default case (since some are disabled by default
// via the dom.disable_window_open_feature pref branch).
const DISALLOWED = {
"location": {
flag: Ci.nsIWebBrowserChrome.CHROME_LOCATIONBAR,
defaults_to: true,
},
"chrome": {
flag: Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME,
defaults_to: false,
},
"dialog": {
flag: Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG,
defaults_to: false,
},
"private": {
flag: Ci.nsIWebBrowserChrome.CHROME_PRIVATE_WINDOW,
defaults_to: false,
},
"non-private": {
flag: Ci.nsIWebBrowserChrome.CHROME_NON_PRIVATE_WINDOW,
defaults_to: false,
},
// "all":
// checked manually, since this is an aggregate
// flag.
//
// "remote":
// checked manually, since its default value will
// depend on whether or not e10s is enabled by default.
"popup": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_POPUP,
defaults_to: false,
},
"alwaysLowered": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED,
defaults_to: false,
},
"z-lock": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED, // Renamed to alwaysLowered
defaults_to: false,
},
"alwaysRaised": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_RAISED,
defaults_to: false,
},
"macsuppressanimation": {
flag: Ci.nsIWebBrowserChrome.CHROME_MAC_SUPPRESS_ANIMATION,
defaults_to: false,
},
"extrachrome": {
flag: Ci.nsIWebBrowserChrome.CHROME_EXTRA,
defaults_to: false,
},
"centerscreen": {
flag: Ci.nsIWebBrowserChrome.CHROME_CENTER_SCREEN,
defaults_to: false,
},
"dependent": {
flag: Ci.nsIWebBrowserChrome.CHROME_DEPENDENT,
defaults_to: false,
},
"modal": {
flag: Ci.nsIWebBrowserChrome.CHROME_MODAL,
defaults_to: false,
},
"titlebar": {
flag: Ci.nsIWebBrowserChrome.CHROME_TITLEBAR,
defaults_to: true,
},
"close": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_CLOSE,
defaults_to: true,
},
"resizable": {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_RESIZE,
defaults_to: true,
},
"status": {
flag: Ci.nsIWebBrowserChrome.CHROME_STATUSBAR,
defaults_to: true,
},
};
// Construct a features string that flips all DISALLOWED features
// to not be their defaults.
const DISALLOWED_STRING = Object.keys(DISALLOWED).map(feature => {
let toValue = DISALLOWED[feature].defaults_to ? "no" : "yes";
return `${feature}=${toValue}`;
}).join(",");
const FEATURES = [ALLOWED_STRING, DISALLOWED_STRING].join(",");
const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank", "${FEATURES}");</script>`;
const SCRIPT_PAGE_FOR_CHROME_ALL = `data:text/html,<script>window.open("about:blank", "_blank", "all");</script>`;
// This magic value of 2 means that by default, when content tries
// to open a new window, it'll actually open in a new window instead
// of a new tab.
Services.prefs.setIntPref("browser.link.open_newwindow", 2);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.link.open_newwindow");
});
/**
* Given some nsIDOMWindow for a window running in the parent
* process, return the nsIWebBrowserChrome chrome flags for
* the associated XUL window.
*
* @param win (nsIDOMWindow)
* Some window in the parent process.
* @returns int
*/
function getParentChromeFlags(win) {
return win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIXULWindow)
.chromeFlags;
}
/**
* For some chromeFlags, ensures that flags that are in the
* ALLOWED group were modified, and that flags in the DISALLOWED
* group were not modified.
*
* @param chromeFlags (int)
* Some chromeFlags to check.
*/
function assertContentFlags(chromeFlags) {
for (let feature in ALLOWED) {
let flag = ALLOWED[feature].flag;
if (ALLOWED[feature].defaults_to) {
// The feature is supposed to default to true, so we should
// have been able to flip it off.
Assert.ok(!(chromeFlags & flag),
`Expected feature ${feature} to be disabled`);
} else {
// The feature is supposed to default to false, so we should
// have been able to flip it on.
Assert.ok((chromeFlags & flag),
`Expected feature ${feature} to be enabled`);
}
}
for (let feature in DISALLOWED) {
let flag = DISALLOWED[feature].flag;
if (DISALLOWED[feature].defaults_to) {
// The feature is supposed to default to true, so it should
// stay true.
Assert.ok((chromeFlags & flag),
`Expected feature ${feature} to be unchanged`);
} else {
// The feature is supposed to default to false, so it should
// stay false.
Assert.ok(!(chromeFlags & flag),
`Expected feature ${feature} to be unchanged`);
}
}
}
/**
* Opens a window from content using window.open with the
* features computed from ALLOWED and DISALLOWED. The computed
* feature string attempts to flip every feature away from their
* default.
*/
add_task(function* test_new_remote_window_flags() {
let newWinPromise = BrowserTestUtils.waitForNewWindow();
yield BrowserTestUtils.withNewTab({
gBrowser,
url: SCRIPT_PAGE,
}, function*(browser) {
let win = yield newWinPromise;
let parentChromeFlags = getParentChromeFlags(win);
assertContentFlags(parentChromeFlags);
if (win.gMultiProcessBrowser) {
Assert.ok(parentChromeFlags &
Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW,
"Should be remote by default");
} else {
Assert.ok(!(parentChromeFlags &
Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW),
"Should not be remote by default");
}
// Confusingly, chromeFlags also exist in the content process
// as part of the TabChild, so we have to check those too.
let b = win.gBrowser.selectedBrowser;
let contentChromeFlags = yield ContentTask.spawn(b, null, function*() {
docShell.QueryInterface(Ci.nsIInterfaceRequestor);
try {
// This will throw if we're not a remote browser.
return docShell.getInterface(Ci.nsITabChild)
.QueryInterface(Ci.nsIWebBrowserChrome)
.chromeFlags;
} catch(e) {
// This must be a non-remote browser...
return docShell.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIWebBrowserChrome)
.chromeFlags;
}
});
assertContentFlags(contentChromeFlags);
Assert.ok(!(contentChromeFlags &
Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW),
"Should not be remote in the content process.");
yield BrowserTestUtils.closeWindow(win);
});
// We check "all" manually, since that's an aggregate flag
// and doesn't fit nicely into the ALLOWED / DISALLOWED scheme
newWinPromise = BrowserTestUtils.waitForNewWindow();
yield BrowserTestUtils.withNewTab({
gBrowser,
url: SCRIPT_PAGE_FOR_CHROME_ALL,
}, function*(browser) {
let win = yield newWinPromise;
let parentChromeFlags = getParentChromeFlags(win);
Assert.notEqual((parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_ALL),
Ci.nsIWebBrowserChrome.CHROME_ALL,
"Should not have been able to set CHROME_ALL");
yield BrowserTestUtils.closeWindow(win);
});
});

View file

@ -0,0 +1,34 @@
"use strict";
/**
* Tests that if chrome-privileged code calls .open() on an
* unprivileged window, that the principal in the newly
* opened window is appropriately set.
*/
add_task(function* test_chrome_opens_window() {
// This magic value of 2 means that by default, when content tries
// to open a new window, it'll actually open in a new window instead
// of a new tab.
yield SpecialPowers.pushPrefEnv({"set": [
["browser.link.open_newwindow", 2],
]});
let newWinPromise = BrowserTestUtils.waitForNewWindow(true, "http://example.com/");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
content.open("http://example.com/", "_blank");
});
let win = yield newWinPromise;
let browser = win.gBrowser.selectedBrowser;
yield ContentTask.spawn(browser, null, function*() {
Assert.ok(!content.document.nodePrincipal.isSystemPrincipal,
"We should not have a system principal.")
Assert.equal(content.document.nodePrincipal.origin,
"http://example.com",
"Should have the example.com principal");
});
yield BrowserTestUtils.closeWindow(win);
});

View file

@ -0,0 +1,78 @@
/**
* Tests that when a remote browser opens a new window that the
* newly opened window is also remote.
*/
const ANCHOR_PAGE = `data:text/html,<a href="about:blank" target="_blank">Click me!</a>`;
const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank");</script>`;
// This magic value of 2 means that by default, when content tries
// to open a new window, it'll actually open in a new window instead
// of a new tab.
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({"set": [
["browser.link.open_newwindow", 2],
]});
});
function assertFlags(win) {
let webNav = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation);
let loadContext = webNav.QueryInterface(Ci.nsILoadContext);
let chromeFlags = webNav.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIXULWindow)
.chromeFlags;
Assert.ok(loadContext.useRemoteTabs,
"Should be using remote tabs on the load context");
Assert.ok(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW,
"Should have the remoteness chrome flag on the window");
}
/**
* Content can open a window using a target="_blank" link
*/
add_task(function* test_new_remote_window_flags_target_blank() {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: ANCHOR_PAGE,
}, function*(browser) {
let newWinPromise = BrowserTestUtils.waitForNewWindow();
yield BrowserTestUtils.synthesizeMouseAtCenter("a", {}, browser);
let win = yield newWinPromise;
assertFlags(win);
yield BrowserTestUtils.closeWindow(win);
});
});
/**
* Content can open a window using window.open
*/
add_task(function* test_new_remote_window_flags_window_open() {
let newWinPromise = BrowserTestUtils.waitForNewWindow();
yield BrowserTestUtils.withNewTab({
gBrowser,
url: SCRIPT_PAGE,
}, function*(browser) {
let win = yield newWinPromise;
assertFlags(win);
yield BrowserTestUtils.closeWindow(win);
});
});
/**
* Privileged content scripts can also open new windows
* using content.open.
*/
add_task(function* test_new_remote_window_flags_content_open() {
let newWinPromise = BrowserTestUtils.waitForNewWindow();
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
content.open("about:blank", "_blank");
});
let win = yield newWinPromise;
assertFlags(win);
yield BrowserTestUtils.closeWindow(win);
});

View file

@ -0,0 +1,67 @@
"use strict";
/**
* Tests that content can open windows at requested dimensions
* of height and width.
*/
/**
* This utility function does most of the actual testing. We
* construct a feature string suitable for the passed in width
* and height, and then run that script in content to open the
* new window. When the new window comes up, this function tests
* to ensure that the content area of the initial browser is the
* requested dimensions. Finally, we also ensure that we're not
* persisting the position, size or sizemode of the new browser
* window.
*/
function test_dimensions({ width, height}) {
let features = [];
if (width) {
features.push(`width=${width}`);
}
if (height) {
features.push(`height=${height}`);
}
const FEATURE_STR = features.join(",");
const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank", "${FEATURE_STR}");</script>`;
let newWinPromise = BrowserTestUtils.waitForNewWindow();
return BrowserTestUtils.withNewTab({
gBrowser,
url: SCRIPT_PAGE,
}, function*(browser) {
let win = yield newWinPromise;
let rect = win.gBrowser.selectedBrowser.getBoundingClientRect();
if (width) {
Assert.equal(rect.width, width, "Should have the requested width");
}
if (height) {
Assert.equal(rect.height, height, "Should have the requested height");
}
let treeOwner = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner;
let persistPosition = {};
let persistSize = {};
let persistSizeMode = {};
treeOwner.getPersistence(persistPosition, persistSize, persistSizeMode);
Assert.ok(!persistPosition.value, "Should not persist position");
Assert.ok(!persistSize.value, "Should not persist size");
Assert.ok(!persistSizeMode.value, "Should not persist size mode");
yield BrowserTestUtils.closeWindow(win);
});
}
add_task(function* test_new_sized_window() {
yield test_dimensions({ width: 100 });
yield test_dimensions({ height: 150 });
yield test_dimensions({ width: 300, height: 200 });
});

View file

@ -0,0 +1,7 @@
[DEFAULT]
tags = openwindow
[test_dialog_arguments.html]
support-files =
file_test_dialog.html
[test_modal_windows.html]

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<!--
This page is opened in a new window by test_storage_copied.html.
We need to return the sessionStorage value for the item "test-item",
by way of postMessage.
-->
<head>
<body>Opened!</body>
<script>
window.postMessage(window.sessionStorage.getItem("test-item"), "*");
</script>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<!--
This page is opened in a new window by test_dialog_arguments. It is
a dialog which expects a Symbol to be passed in the dialog arguments.
Once we load, we call back into the opener with the argument we were
passed.
-->
<head>
<body>Opened!</body>
<script>
window.opener.done(window.arguments[0]);
</script>
</html>

View file

@ -0,0 +1,12 @@
[DEFAULT]
tags = openwindow
[test_blank_named_window.html]
skip-if = (os == 'android') # Fennec doesn't support web content opening new windows (See bug 1277544 for details)
[test_named_window.html]
skip-if = (os == 'android') # Fennec doesn't support web content opening new windows (See bug 1277544 for details)
[test_storage_copied.html]
support-files =
file_storage_copied.html
skip-if = (os == 'android') # Fennec doesn't support web content opening new windows (See bug 1277544 for details)

View file

@ -0,0 +1,18 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
BROWSER_CHROME_MANIFESTS += [
'browser.ini',
]
MOCHITEST_MANIFESTS += [
'mochitest.ini',
]
MOCHITEST_CHROME_MANIFESTS += [
'chrome.ini',
]

View file

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<!--
Test that when opening a window with the reserved name _blank that the new
window does not get that name, and that subsequent window openings with that
name result in new windows being opened.
-->
<head>
<meta charset="utf-8">
<title>Test named windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script src="head.js" type="application/javascript;version=1.8"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
"use strict";
add_task(function*() {
// This magic value of 2 means that by default, when content tries
// to open a new window, it'll actually open in a new window instead
// of a new tab.
yield SpecialPowers.pushPrefEnv({"set": [
["browser.link.open_newwindow", 2],
]});
let win1 = window.open("data:text/html,<p>This is window 1 for test_blank_named_window.html</p>", "_blank");
let name = SpecialPowers.wrap(win1)
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
.QueryInterface(SpecialPowers.Ci.nsIDocShellTreeItem)
.name;
is(name, "", "Should have no name");
let win2 = window.open("data:text/html,<p>This is window 2 for test_blank_named_window.html</p>", "_blank");
isnot(win1, win2, "Should not have gotten back the same window");
win1.close();
win2.close();
});
</script>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<!--
Test that arguments can be passed to dialogs.
-->
<head>
<meta charset="utf-8">
<title>Test a modal window</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8">
const {utils: Cu, interfaces: Ci} = Components;
Cu.import("resource://gre/modules/Services.jsm");
const TEST_ITEM = Symbol("test-item");
function done(returnedItem) {
is(returnedItem, TEST_ITEM,
"Dialog should have received test item");
win.close();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
let win = window.openDialog("file_test_dialog.html", "_blank", "width=100,height=100", TEST_ITEM);
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<html>
<!--
Test that the parent can open modal windows, and that the modal window
that is opened reports itself as being modal.
-->
<head>
<meta charset="utf-8">
<title>Test a modal window</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8">
const {utils: Cu, interfaces: Ci} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://testing-common/BrowserTestUtils.jsm");
add_task(function*() {
BrowserTestUtils.domWindowOpened().then((win) => {
let treeOwner = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
let chromeFlags = treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIXULWindow)
.chromeFlags;
ok(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_MODAL,
"Should have the modal chrome flag");
let wbc = treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebBrowserChrome);
ok(wbc.isWindowModal(), "Should report as modal");
win.close();
});
let modal = window.openDialog("data:text/html,<p>This is a modal window for test_modal_windows.html</p>",
"_blank", "modal", null);
// Since the modal runs a nested event loop, just to be on the safe side,
// we'll wait a tick of the main event loop before resolving the task.
yield new Promise(resolve => setTimeout(resolve, 0));
});
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View file

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<!--
Test that when content opens a new window with a name, that the
newly opened window actually gets that name, and that subsequent
attempts to open a window with that name will target the same
window.
-->
<head>
<meta charset="utf-8">
<title>Test named windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script src="head.js" type="application/javascript;version=1.8"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a href="#" id="link">Click me</a>
<script type="application/javascript">
"use strict";
const NAME = "my_window";
const TARGET_URL = "data:text/html,<html><body>test_named_window.html new window</body></html>";
const TARGET_URL_2 = TARGET_URL + "#2";
const TARGET_URL_3 = TARGET_URL + "#3";
/**
* Returns a Promise that resolves once some target has had
* some event dispatched on it.
*
* @param target
* The thing to wait for the event to be dispatched
* through.
* @param eventName
* The name of the event to wait for.
* @returns Promise
*/
function promiseEvent(target, eventName) {
return new Promise(resolve => {
target.addEventListener(eventName, function onEvent(e) {
target.removeEventListener(eventName, onEvent, true);
resolve(e);
}, true);
});
}
add_task(function*() {
// This magic value of 2 means that by default, when content tries
// to open a new window, it'll actually open in a new window instead
// of a new tab.
yield SpecialPowers.pushPrefEnv({"set": [
["browser.link.open_newwindow", 2],
]});
let win1 = window.open(TARGET_URL, "my_window");
yield promiseEvent(win1, "load");
let name = SpecialPowers.wrap(win1)
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
.QueryInterface(SpecialPowers.Ci.nsIDocShellTreeItem)
.name;
is(name, NAME, "Should have the expected name");
is(win1.location.href, new URL(TARGET_URL).href,
"Should have loaded target TARGET_URL in the original window");
let hashChange = promiseEvent(win1, "hashchange");
let win2 = window.open(TARGET_URL_2, "my_window");
yield hashChange;
is(win1, win2, "Should have gotten back the same window");
is(win1.location.href, new URL(TARGET_URL_2).href,
"Should have re-targeted pre-existing window");
hashChange = promiseEvent(win1, "hashchange");
let link = document.getElementById("link");
link.setAttribute("target", NAME);
link.setAttribute("href", TARGET_URL_3);
link.click();
yield hashChange;
is(win1.location.href, new URL(TARGET_URL_3).href,
"Should have re-targeted pre-existing window");
win1.close();
});
</script>
</body>
</html>

View file

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<!--
Test sessionStorage is copied over when a new window opens to the
same domain as the opener.
-->
<head>
<meta charset="utf-8">
<title>Test storage copied</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script src="head.js" type="application/javascript;version=1.8"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
"use strict";
function waitForMessage(win) {
return new Promise(resolve => {
win.addEventListener("message", function onMessage(event) {
win.removeEventListener("message", onMessage);
resolve(event.data);
});
});
}
add_task(function*() {
const TEST_VALUE = "test-value";
// This magic value of 2 means that by default, when content tries
// to open a new window, it'll actually open in a new window instead
// of a new tab.
yield SpecialPowers.pushPrefEnv({"set": [
["browser.link.open_newwindow", 2],
]});
window.sessionStorage.setItem("test-item", TEST_VALUE);
let win = window.open("file_storage_copied.html", "my_window");
let data = yield waitForMessage(win);
is(data, TEST_VALUE, "Should have cloned the test value");
win.close();
});
</script>
</body>
</html>