Bug Summary

File:src/apps/webpositive/BrowserWindow.cpp
Location:line 2029, column 2
Description:Potential leak of memory pointed to by 'threeDaysAgoMenu'

Annotated Source Code

1/*
2 * Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com>
3 * Copyright (C) 2007, 2010 Ryan Leavengood <leavengood@gmail.com>
4 * Copyright (C) 2009 Maxime Simon <simon.maxime@gmail.com>
5 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
6 * Copyright (C) 2010 Michael Lotz <mmlr@mlotz.ch>
7 * Copyright (C) 2010 Rene Gollent <rene@gollent.com>
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "BrowserWindow.h"
34
35#include <Alert.h>
36#include <Application.h>
37#include <Bitmap.h>
38#include <Button.h>
39#include <Catalog.h>
40#include <CheckBox.h>
41#include <Clipboard.h>
42#include <ControlLook.h>
43#include <Debug.h>
44#include <Directory.h>
45#include <Entry.h>
46#include <File.h>
47#include <FindDirectory.h>
48#include <GridLayoutBuilder.h>
49#include <GroupLayout.h>
50#include <GroupLayoutBuilder.h>
51#include <LayoutBuilder.h>
52#include <Locale.h>
53#include <MenuBar.h>
54#include <MenuItem.h>
55#include <MessageRunner.h>
56#include <NodeInfo.h>
57#include <Path.h>
58#include <Roster.h>
59#include <Screen.h>
60#include <SeparatorView.h>
61#include <Size.h>
62#include <SpaceLayoutItem.h>
63#include <StatusBar.h>
64#include <StringView.h>
65#include <TextControl.h>
66
67#include <stdio.h>
68
69#include "AuthenticationPanel.h"
70#include "BaseURL.h"
71#include "BitmapButton.h"
72#include "BrowserApp.h"
73#include "BrowsingHistory.h"
74#include "CredentialsStorage.h"
75#include "IconButton.h"
76#include "NavMenu.h"
77#include "SettingsKeys.h"
78#include "SettingsMessage.h"
79#include "TabManager.h"
80#include "URLInputGroup.h"
81#include "WebPage.h"
82#include "WebView.h"
83#include "WebViewConstants.h"
84#include "WindowIcon.h"
85
86
87#undef B_TRANSLATION_CONTEXT"WebPositive Window"
88#define B_TRANSLATION_CONTEXT"WebPositive Window" "WebPositive Window"
89
90
91enum {
92 OPEN_LOCATION = 'open',
93 GO_BACK = 'goba',
94 GO_FORWARD = 'gofo',
95 STOP = 'stop',
96 HOME = 'home',
97 GOTO_URL = 'goul',
98 RELOAD = 'reld',
99 CLEAR_HISTORY = 'clhs',
100
101 CREATE_BOOKMARK = 'crbm',
102 SHOW_BOOKMARKS = 'shbm',
103
104 ZOOM_FACTOR_INCREASE = 'zfin',
105 ZOOM_FACTOR_DECREASE = 'zfdc',
106 ZOOM_FACTOR_RESET = 'zfrs',
107 ZOOM_TEXT_ONLY = 'zfto',
108
109 TOGGLE_FULLSCREEN = 'tgfs',
110 TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN = 'tgah',
111 CHECK_AUTO_HIDE_INTERFACE = 'cahi',
112
113 SHOW_PAGE_SOURCE = 'spgs',
114
115 EDIT_SHOW_FIND_GROUP = 'sfnd',
116 EDIT_HIDE_FIND_GROUP = 'hfnd',
117 EDIT_FIND_NEXT = 'fndn',
118 EDIT_FIND_PREVIOUS = 'fndp',
119 FIND_TEXT_CHANGED = 'ftxt',
120
121 SELECT_TAB = 'sltb',
122};
123
124
125static BLayoutItem*
126layoutItemFor(BView* view)
127{
128 BLayout* layout = view->Parent()->GetLayout();
129 int32 index = layout->IndexOfView(view);
130 return layout->ItemAt(index);
131}
132
133
134class BookmarkMenu : public BNavMenu {
135public:
136 BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir)
137 :
138 BNavMenu(title, B_REFS_RECEIVED, target)
139 {
140 // Add these items here already, so the shortcuts work even when
141 // the menu has never been opened yet.
142 _AddStaticItems();
143
144 SetNavDir(navDir);
145 }
146
147 virtual void AttachedToWindow()
148 {
149 RemoveItems(0, CountItems(), true);
150 ForceRebuild();
151 BNavMenu::AttachedToWindow();
152 if (CountItems() > 0)
153 AddItem(new BSeparatorItem(), 0);
154 _AddStaticItems();
155 DoLayout();
156 }
157
158private:
159 void _AddStaticItems()
160 {
161 AddItem(new BMenuItem(B_TRANSLATE("Manage bookmarks")BLocaleRoster::Default()->GetCatalog()->GetString(("Manage bookmarks"
), "WebPositive Window")
,
162 new BMessage(SHOW_BOOKMARKS), 'M'), 0);
163 AddItem(new BMenuItem(B_TRANSLATE("Bookmark this page")BLocaleRoster::Default()->GetCatalog()->GetString(("Bookmark this page"
), "WebPositive Window")
,
164 new BMessage(CREATE_BOOKMARK), 'B'), 0);
165 }
166};
167
168
169class PageUserData : public BWebView::UserData {
170public:
171 PageUserData(BView* focusedView)
172 :
173 fFocusedView(focusedView),
174 fPageIcon(NULL__null),
175 fURLInputSelectionStart(-1),
176 fURLInputSelectionEnd(-1)
177 {
178 }
179
180 ~PageUserData()
181 {
182 delete fPageIcon;
183 }
184
185 void SetFocusedView(BView* focusedView)
186 {
187 fFocusedView = focusedView;
188 }
189
190 BView* FocusedView() const
191 {
192 return fFocusedView;
193 }
194
195 void SetPageIcon(const BBitmap* icon)
196 {
197 delete fPageIcon;
198 if (icon)
199 fPageIcon = new BBitmap(icon);
200 else
201 fPageIcon = NULL__null;
202 }
203
204 const BBitmap* PageIcon() const
205 {
206 return fPageIcon;
207 }
208
209 void SetURLInputContents(const char* text)
210 {
211 fURLInputContents = text;
212 }
213
214 const BString& URLInputContents() const
215 {
216 return fURLInputContents;
217 }
218
219 void SetURLInputSelection(int32 selectionStart, int32 selectionEnd)
220 {
221 fURLInputSelectionStart = selectionStart;
222 fURLInputSelectionEnd = selectionEnd;
223 }
224
225 int32 URLInputSelectionStart() const
226 {
227 return fURLInputSelectionStart;
228 }
229
230 int32 URLInputSelectionEnd() const
231 {
232 return fURLInputSelectionEnd;
233 }
234
235private:
236 BView* fFocusedView;
237 BBitmap* fPageIcon;
238 BString fURLInputContents;
239 int32 fURLInputSelectionStart;
240 int32 fURLInputSelectionEnd;
241};
242
243
244class CloseButton : public BButton {
245public:
246 CloseButton(BMessage* message)
247 :
248 BButton("close button", NULL__null, message),
249 fOverCloseRect(false)
250 {
251 // Button is 16x16 regardless of font size
252 SetExplicitMinSize(BSize(15, 15));
253 SetExplicitMaxSize(BSize(15, 15));
254 }
255
256 virtual void Draw(BRect updateRect)
257 {
258 BRect frame = Bounds();
259 BRect closeRect(frame.InsetByCopy(4, 4));
260 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
261 float tint = B_DARKEN_1_TINT;
262
263 if (fOverCloseRect)
264 tint *= 1.4;
265 else
266 tint *= 1.2;
267
268 if (Value() == B_CONTROL_ON && fOverCloseRect) {
269 // Draw the button frame
270 be_control_look->DrawButtonFrame(this, frame, updateRect,
271 base, base, BControlLook::B_ACTIVATED
272 | BControlLook::B_BLEND_FRAME);
273 be_control_look->DrawButtonBackground(this, frame,
274 updateRect, base, BControlLook::B_ACTIVATED);
275 closeRect.OffsetBy(1, 1);
276 tint *= 1.2;
277 } else {
278 SetHighColor(base);
279 FillRect(updateRect);
280 }
281
282 // Draw the ×
283 base = tint_color(base, tint);
284 SetHighColor(base);
285 SetPenSize(2);
286 StrokeLine(closeRect.LeftTop(), closeRect.RightBottom());
287 StrokeLine(closeRect.LeftBottom(), closeRect.RightTop());
288 SetPenSize(1);
289 }
290
291 virtual void MouseMoved(BPoint where, uint32 transit,
292 const BMessage* dragMessage)
293 {
294 switch (transit) {
295 case B_ENTERED_VIEW:
296 fOverCloseRect = true;
297 Invalidate();
298 break;
299 case B_EXITED_VIEW:
300 fOverCloseRect = false;
301 Invalidate();
302 break;
303 case B_INSIDE_VIEW:
304 fOverCloseRect = true;
305 break;
306 case B_OUTSIDE_VIEW:
307 fOverCloseRect = false;
308 break;
309 }
310
311 BButton::MouseMoved(where, transit, dragMessage);
312 }
313
314private:
315 bool fOverCloseRect;
316};
317
318
319// #pragma mark - BrowserWindow
320
321
322BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings,
323 const BString& url, uint32 interfaceElements, BWebView* webView)
324 :
325 BWebWindow(frame, kApplicationName,
326 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
327 B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS),
328 fIsFullscreen(false),
329 fInterfaceVisible(false),
330 fPulseRunner(NULL__null),
331 fVisibleInterfaceElements(interfaceElements),
332 fAppSettings(appSettings),
333 fZoomTextOnly(true),
334 fShowTabsIfSinglePageOpen(true),
335 fAutoHideInterfaceInFullscreenMode(false),
336 fAutoHidePointer(false)
337{
338 // Begin listening to settings changes and read some current values.
339 fAppSettings->AddListener(BMessenger(this));
340// fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly);
341 fShowTabsIfSinglePageOpen = fAppSettings->GetValue(
342 kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen);
343
344 fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer,
345 fAutoHidePointer);
346
347 fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy,
348 (uint32)OpenStartPage);
349 fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy,
350 (uint32)OpenBlankPage);
351 fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL,
352 kDefaultStartPageURL);
353 fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL,
354 kDefaultSearchPageURL);
355
356 // Create the interface elements
357 BMessage* newTabMessage = new BMessage(NEW_TAB);
358 newTabMessage->AddString("url", "");
359 newTabMessage->AddPointer("window", this);
360 newTabMessage->AddBool("select", true);
361 fTabManager = new TabManager(BMessenger(this), newTabMessage);
362
363 // Menu
364#if INTEGRATE_MENU_INTO_TAB_BAR0
365 BMenu* mainMenu = fTabManager->Menu();
366#else
367 BMenu* mainMenu = new BMenuBar("Main menu");
368#endif
369 BMenu* menu = new BMenu(B_TRANSLATE("Window")BLocaleRoster::Default()->GetCatalog()->GetString(("Window"
), "WebPositive Window")
);
370 BMessage* newWindowMessage = new BMessage(NEW_WINDOW);
371 newWindowMessage->AddString("url", "");
372 BMenuItem* newItem = new BMenuItem(B_TRANSLATE("New window")BLocaleRoster::Default()->GetCatalog()->GetString(("New window"
), "WebPositive Window")
,
373 newWindowMessage, 'N');
374 menu->AddItem(newItem);
375 newItem->SetTarget(be_app);
376 newItem = new BMenuItem(B_TRANSLATE("New tab")BLocaleRoster::Default()->GetCatalog()->GetString(("New tab"
), "WebPositive Window")
,
377 new BMessage(*newTabMessage), 'T');
378 menu->AddItem(newItem);
379 newItem->SetTarget(be_app);
380 menu->AddItem(new BMenuItem(B_TRANSLATE("Open location")BLocaleRoster::Default()->GetCatalog()->GetString(("Open location"
), "WebPositive Window")
,
381 new BMessage(OPEN_LOCATION), 'L'));
382 menu->AddSeparatorItem();
383 menu->AddItem(new BMenuItem(B_TRANSLATE("Close window")BLocaleRoster::Default()->GetCatalog()->GetString(("Close window"
), "WebPositive Window")
,
384 new BMessage(B_QUIT_REQUESTED), 'W', B_SHIFT_KEY));
385 menu->AddItem(new BMenuItem(B_TRANSLATE("Close tab")BLocaleRoster::Default()->GetCatalog()->GetString(("Close tab"
), "WebPositive Window")
,
386 new BMessage(CLOSE_TAB), 'W'));
387 menu->AddSeparatorItem();
388 menu->AddItem(new BMenuItem(B_TRANSLATE("Downloads")BLocaleRoster::Default()->GetCatalog()->GetString(("Downloads"
), "WebPositive Window")
,
389 new BMessage(SHOW_DOWNLOAD_WINDOW), 'D'));
390 menu->AddItem(new BMenuItem(B_TRANSLATE("Settings")BLocaleRoster::Default()->GetCatalog()->GetString(("Settings"
), "WebPositive Window")
,
391 new BMessage(SHOW_SETTINGS_WINDOW)));
392 BMenuItem* aboutItem = new BMenuItem(B_TRANSLATE("About")BLocaleRoster::Default()->GetCatalog()->GetString(("About"
), "WebPositive Window")
,
393 new BMessage(B_ABOUT_REQUESTED));
394 menu->AddItem(aboutItem);
395 aboutItem->SetTarget(be_app);
396 menu->AddSeparatorItem();
397 BMenuItem* quitItem = new BMenuItem(B_TRANSLATE("Quit")BLocaleRoster::Default()->GetCatalog()->GetString(("Quit"
), "WebPositive Window")
,
398 new BMessage(B_QUIT_REQUESTED), 'Q');
399 menu->AddItem(quitItem);
400 quitItem->SetTarget(be_app);
401 mainMenu->AddItem(menu);
402
403 menu = new BMenu(B_TRANSLATE("Edit")BLocaleRoster::Default()->GetCatalog()->GetString(("Edit"
), "WebPositive Window")
);
404 menu->AddItem(fCutMenuItem = new BMenuItem(B_TRANSLATE("Cut")BLocaleRoster::Default()->GetCatalog()->GetString(("Cut"
), "WebPositive Window")
,
405 new BMessage(B_CUT), 'X'));
406 menu->AddItem(fCopyMenuItem = new BMenuItem(B_TRANSLATE("Copy")BLocaleRoster::Default()->GetCatalog()->GetString(("Copy"
), "WebPositive Window")
,
407 new BMessage(B_COPY), 'C'));
408 menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste")BLocaleRoster::Default()->GetCatalog()->GetString(("Paste"
), "WebPositive Window")
,
409 new BMessage(B_PASTE), 'V'));
410 menu->AddSeparatorItem();
411 menu->AddItem(new BMenuItem(B_TRANSLATE("Find")BLocaleRoster::Default()->GetCatalog()->GetString(("Find"
), "WebPositive Window")
,
412 new BMessage(EDIT_SHOW_FIND_GROUP), 'F'));
413 menu->AddItem(fFindPreviousMenuItem
414 = new BMenuItem(B_TRANSLATE("Find previous")BLocaleRoster::Default()->GetCatalog()->GetString(("Find previous"
), "WebPositive Window")
,
415 new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY));
416 menu->AddItem(fFindNextMenuItem = new BMenuItem(B_TRANSLATE("Find next")BLocaleRoster::Default()->GetCatalog()->GetString(("Find next"
), "WebPositive Window")
,
417 new BMessage(EDIT_FIND_NEXT), 'G'));
418 mainMenu->AddItem(menu);
419 fFindPreviousMenuItem->SetEnabled(false);
420 fFindNextMenuItem->SetEnabled(false);
421
422 menu = new BMenu(B_TRANSLATE("View")BLocaleRoster::Default()->GetCatalog()->GetString(("View"
), "WebPositive Window")
);
423 menu->AddItem(new BMenuItem(B_TRANSLATE("Reload")BLocaleRoster::Default()->GetCatalog()->GetString(("Reload"
), "WebPositive Window")
, new BMessage(RELOAD),
424 'R'));
425 menu->AddSeparatorItem();
426 menu->AddItem(new BMenuItem(B_TRANSLATE("Increase size")BLocaleRoster::Default()->GetCatalog()->GetString(("Increase size"
), "WebPositive Window")
,
427 new BMessage(ZOOM_FACTOR_INCREASE), '+'));
428 menu->AddItem(new BMenuItem(B_TRANSLATE("Decrease size")BLocaleRoster::Default()->GetCatalog()->GetString(("Decrease size"
), "WebPositive Window")
,
429 new BMessage(ZOOM_FACTOR_DECREASE), '-'));
430 menu->AddItem(new BMenuItem(B_TRANSLATE("Reset size")BLocaleRoster::Default()->GetCatalog()->GetString(("Reset size"
), "WebPositive Window")
,
431 new BMessage(ZOOM_FACTOR_RESET), '0'));
432 fZoomTextOnlyMenuItem = new BMenuItem(B_TRANSLATE("Zoom text only")BLocaleRoster::Default()->GetCatalog()->GetString(("Zoom text only"
), "WebPositive Window")
,
433 new BMessage(ZOOM_TEXT_ONLY));
434 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly);
435 menu->AddItem(fZoomTextOnlyMenuItem);
436
437 menu->AddSeparatorItem();
438 fFullscreenItem = new BMenuItem(B_TRANSLATE("Full screen")BLocaleRoster::Default()->GetCatalog()->GetString(("Full screen"
), "WebPositive Window")
,
439 new BMessage(TOGGLE_FULLSCREEN), B_RETURN);
440 menu->AddItem(fFullscreenItem);
441 menu->AddItem(new BMenuItem(B_TRANSLATE("Page source")BLocaleRoster::Default()->GetCatalog()->GetString(("Page source"
), "WebPositive Window")
,
442 new BMessage(SHOW_PAGE_SOURCE), 'U'));
443 mainMenu->AddItem(menu);
444
445 fHistoryMenu = new BMenu(B_TRANSLATE("History")BLocaleRoster::Default()->GetCatalog()->GetString(("History"
), "WebPositive Window")
);
446 fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem(B_TRANSLATE("Back")BLocaleRoster::Default()->GetCatalog()->GetString(("Back"
), "WebPositive Window")
,
447 new BMessage(GO_BACK), B_LEFT_ARROW));
448 fHistoryMenu->AddItem(fForwardMenuItem
449 = new BMenuItem(B_TRANSLATE("Forward")BLocaleRoster::Default()->GetCatalog()->GetString(("Forward"
), "WebPositive Window")
, new BMessage(GO_FORWARD),
450 B_RIGHT_ARROW));
451 fHistoryMenu->AddSeparatorItem();
452 fHistoryMenuFixedItemCount = fHistoryMenu->CountItems();
453 mainMenu->AddItem(fHistoryMenu);
454
455 BPath bookmarkPath;
456 entry_ref bookmarkRef;
457 if (_BookmarkPath(bookmarkPath) == B_OK((int)0)
458 && get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK((int)0)) {
459 BMenu* bookmarkMenu
460 = new BookmarkMenu(B_TRANSLATE("Bookmarks")BLocaleRoster::Default()->GetCatalog()->GetString(("Bookmarks"
), "WebPositive Window")
, this, &bookmarkRef);
461 mainMenu->AddItem(bookmarkMenu);
462 }
463
464 // Back, Forward, Stop & Home buttons
465 fBackButton = new BIconButton("Back", NULL__null, new BMessage(GO_BACK));
466 fBackButton->SetIcon(201);
467 fBackButton->TrimIcon();
468
469 fForwardButton = new BIconButton("Forward", NULL__null, new BMessage(GO_FORWARD));
470 fForwardButton->SetIcon(202);
471 fForwardButton->TrimIcon();
472
473 fStopButton = new BIconButton("Stop", NULL__null, new BMessage(STOP));
474 fStopButton->SetIcon(204);
475 fStopButton->TrimIcon();
476
477 fHomeButton = new BIconButton("Home", NULL__null, new BMessage(HOME));
478 fHomeButton->SetIcon(206);
479 fHomeButton->TrimIcon();
480 if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true))
481 fHomeButton->Hide();
482
483 // URL input group
484 fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL));
485
486 // Status Bar
487 fStatusText = new BStringView("status", "");
488 fStatusText->SetAlignment(B_ALIGN_LEFT);
489 fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
490 fStatusText->SetExplicitMinSize(BSize(150, 12));
491 // Prevent the window from growing to fit a long status message...
492 BFont font(be_plain_font);
493 font.SetSize(ceilf(font.Size() * 0.8));
494 fStatusText->SetFont(&font, B_FONT_SIZE);
495
496 // Loading progress bar
497 fLoadingProgressBar = new BStatusBar("progress");
498 fLoadingProgressBar->SetMaxValue(100);
499 fLoadingProgressBar->Hide();
500 fLoadingProgressBar->SetBarHeight(12);
501
502 const float kInsetSpacing = 3;
503 const float kElementSpacing = 5;
504
505 // Find group
506 fFindCloseButton = new CloseButton(new BMessage(EDIT_HIDE_FIND_GROUP));
507 fFindTextControl = new BTextControl("find", B_TRANSLATE("Find:")BLocaleRoster::Default()->GetCatalog()->GetString(("Find:"
), "WebPositive Window")
, "",
508 new BMessage(EDIT_FIND_NEXT));
509 fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED));
510 fFindPreviousButton = new BButton(B_TRANSLATE("Previous")BLocaleRoster::Default()->GetCatalog()->GetString(("Previous"
), "WebPositive Window")
,
511 new BMessage(EDIT_FIND_PREVIOUS));
512 fFindPreviousButton->SetToolTip(
513 B_TRANSLATE_COMMENT("Find previous occurrence of search terms",BLocaleRoster::Default()->GetCatalog()->GetString(("Find previous occurrence of search terms"
), "WebPositive Window", ("find bar previous button tooltip")
)
514 "find bar previous button tooltip")BLocaleRoster::Default()->GetCatalog()->GetString(("Find previous occurrence of search terms"
), "WebPositive Window", ("find bar previous button tooltip")
)
);
515 fFindNextButton = new BButton(B_TRANSLATE("Next")BLocaleRoster::Default()->GetCatalog()->GetString(("Next"
), "WebPositive Window")
,
516 new BMessage(EDIT_FIND_NEXT));
517 fFindNextButton->SetToolTip(
518 B_TRANSLATE_COMMENT("Find next occurrence of search terms",BLocaleRoster::Default()->GetCatalog()->GetString(("Find next occurrence of search terms"
), "WebPositive Window", ("find bar next button tooltip"))
519 "find bar next button tooltip")BLocaleRoster::Default()->GetCatalog()->GetString(("Find next occurrence of search terms"
), "WebPositive Window", ("find bar next button tooltip"))
);
520 fFindCaseSensitiveCheckBox = new BCheckBox(B_TRANSLATE("Match case")BLocaleRoster::Default()->GetCatalog()->GetString(("Match case"
), "WebPositive Window")
);
521 BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
522 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
523 .Add(BGroupLayoutBuilder(B_HORIZONTAL, B_USE_SMALL_SPACING)
524 .Add(fFindCloseButton)
525 .Add(fFindTextControl)
526 .Add(fFindPreviousButton)
527 .Add(fFindNextButton)
528 .Add(fFindCaseSensitiveCheckBox)
529 .SetInsets(kInsetSpacing, kInsetSpacing,
530 kInsetSpacing, kInsetSpacing)
531 )
532 ;
533
534 // Navigation group
535 BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
536 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing)
537 .Add(fBackButton)
538 .Add(fForwardButton)
539 .Add(fStopButton)
540 .Add(fHomeButton)
541 .Add(fURLInputGroup)
542 .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing,
543 kInsetSpacing)
544 )
545 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
546 ;
547
548 // Status bar group
549 BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
550 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
551 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing)
552 .Add(fStatusText)
553 .Add(fLoadingProgressBar, 0.2)
554 .AddStrut(12 - kElementSpacing)
555 .SetInsets(kInsetSpacing, 0, kInsetSpacing, 0)
556 )
557 ;
558
559 BitmapButton* toggleFullscreenButton = new BitmapButton(kWindowIconBits,
560 kWindowIconWidth, kWindowIconHeight, kWindowIconFormat,
561 new BMessage(TOGGLE_FULLSCREEN));
562 toggleFullscreenButton->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND);
563
564 BGroupLayout* menuBarGroup = BLayoutBuilder::Group<>(B_HORIZONTAL, 0.0)
565 .Add(mainMenu)
566 .Add(toggleFullscreenButton, 0.0f)
567 ;
568
569 // Layout
570 AddChild(BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
571#if !INTEGRATE_MENU_INTO_TAB_BAR0
572 .Add(menuBarGroup)
573#endif
574 .Add(fTabManager->TabGroup())
575 .Add(navigationGroup)
576 .Add(fTabManager->ContainerView())
577 .Add(findGroup)
578 .Add(statusGroup)
579 );
580
581 fURLInputGroup->MakeFocus(true);
582
583 fMenuGroup = menuBarGroup;
584 fTabGroup = fTabManager->TabGroup()->GetLayout();
585 fNavigationGroup = navigationGroup;
586 fFindGroup = findGroup;
587 fStatusGroup = statusGroup;
588 fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton);
589
590 fFindGroup->SetVisible(false);
591 fToggleFullscreenButton->SetVisible(false);
592
593 CreateNewTab(url, true, webView);
594 _ShowInterface(true);
595 _SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue(
596 kSettingsKeyAutoHideInterfaceInFullscreenMode,
597 fAutoHideInterfaceInFullscreenMode));
598
599 AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY,
600 new BMessage(EDIT_HIDE_FIND_GROUP));
601 // TODO: Should be a different shortcut, H is usually for Find selection.
602 AddShortcut('H', B_COMMAND_KEY, new BMessage(HOME));
603
604 // Add shortcuts to select a particular tab
605 for (int32 i = 1; i <= 9; i++) {
606 BMessage* selectTab = new BMessage(SELECT_TAB);
607 selectTab->AddInt32("tab index", i - 1);
608 char numStr[2];
609 snprintf(numStr, sizeof(numStr), "%d", (int) i);
610 AddShortcut(numStr[0], B_COMMAND_KEY, selectTab);
611 }
612
613 be_app->PostMessage(WINDOW_OPENED);
614}
615
616
617BrowserWindow::~BrowserWindow()
618{
619 fAppSettings->RemoveListener(BMessenger(this));
620 delete fTabManager;
621 delete fPulseRunner;
622}
623
624
625void
626BrowserWindow::DispatchMessage(BMessage* message, BHandler* target)
627{
628 const char* bytes;
629 uint32 modifierKeys;
630 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)
631 && message->FindString("bytes", &bytes) == B_OK((int)0)
632 && message->FindInt32("modifiers", (int32*)&modifierKeys) == B_OK((int)0)) {
633
634 modifierKeys = modifierKeys & 0x000000ff;
635 if (bytes[0] == B_LEFT_ARROW && modifierKeys == B_COMMAND_KEY) {
636 PostMessage(GO_BACK);
637 return;
638 } else if (bytes[0] == B_RIGHT_ARROW && modifierKeys == B_COMMAND_KEY) {
639 PostMessage(GO_FORWARD);
640 return;
641 } else if (bytes[0] == B_FUNCTION_KEY) {
642 // Some function key Firefox compatibility
643 int32 key;
644 if (message->FindInt32("key", &key) == B_OK((int)0)) {
645 switch (key) {
646 case B_F5_KEY:
647 PostMessage(RELOAD);
648 break;
649 case B_F11_KEY:
650 PostMessage(TOGGLE_FULLSCREEN);
651 break;
652 default:
653 break;
654 }
655 }
656 } else if (target == fURLInputGroup->TextView()) {
657 // Handle B_RETURN in the URL text control. This is the easiest
658 // way to react *only* when the user presses the return key in the
659 // address bar, as opposed to trying to load whatever is in there
660 // when the text control just goes out of focus.
661 if (bytes[0] == B_RETURN) {
662 // Do it in such a way that the user sees the Go-button go down.
663 _InvokeButtonVisibly(fURLInputGroup->GoButton());
664 return;
665 }
666 } else if (target == fFindTextControl->TextView()) {
667 // Handle B_RETURN when the find text control has focus.
668 if (bytes[0] == B_RETURN) {
669 if ((modifierKeys & B_SHIFT_KEY) != 0)
670 _InvokeButtonVisibly(fFindPreviousButton);
671 else
672 _InvokeButtonVisibly(fFindNextButton);
673 return;
674 } else if (bytes[0] == B_ESCAPE) {
675 _InvokeButtonVisibly(fFindCloseButton);
676 return;
677 }
678 } else if (bytes[0] == B_ESCAPE) {
679 // Default escape key behavior:
680 PostMessage(STOP);
681 return;
682 }
683 }
684 if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN
685 || message->what == B_MOUSE_UP) {
686 message->FindPoint("where", &fLastMousePos);
687 if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK((int)0))
688 fLastMouseMovedTime = system_time();
689 _CheckAutoHideInterface();
690 }
691 if (message->what == B_MOUSE_WHEEL_CHANGED) {
692 BPoint where;
693 uint32 buttons;
694 CurrentWebView()->GetMouse(&where, &buttons, false);
695 // Only do this when the mouse is over the web view
696 if (CurrentWebView()->Bounds().Contains(where)) {
697 // Zoom and unzoom text on Command + mouse wheel.
698 // This could of course (and maybe should be) implemented in the
699 // WebView, but there would need to be a way for the WebView to
700 // know the setting of the fZoomTextOnly member here. Plus other
701 // clients of the API may not want this feature.
702 if ((modifiers() & B_COMMAND_KEY) != 0) {
703 float dy;
704 if (message->FindFloat("be:wheel_delta_y", &dy) == B_OK((int)0)) {
705 if (dy < 0)
706 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly);
707 else
708 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly);
709 return;
710 }
711 }
712 } else // Also don't scroll up and down if the mouse is not over the web view
713 return;
714 }
715 BWebWindow::DispatchMessage(message, target);
716}
717
718
719void
720BrowserWindow::MessageReceived(BMessage* message)
721{
722 switch (message->what) {
723 case OPEN_LOCATION:
724 _ShowInterface(true);
725 if (fURLInputGroup->TextView()->IsFocus())
726 fURLInputGroup->TextView()->SelectAll();
727 else
728 fURLInputGroup->MakeFocus(true);
729 break;
730 case RELOAD:
731 CurrentWebView()->Reload();
732 break;
733 case GOTO_URL:
734 {
735 BString url;
736 if (message->FindString("url", &url) != B_OK((int)0))
737 url = fURLInputGroup->Text();
738
739 _SetPageIcon(CurrentWebView(), NULL__null);
740 _SmartURLHandler(url);
741
742 break;
743 }
744 case GO_BACK:
745 CurrentWebView()->GoBack();
746 break;
747 case GO_FORWARD:
748 CurrentWebView()->GoForward();
749 break;
750 case STOP:
751 CurrentWebView()->StopLoading();
752 break;
753 case HOME:
754 CurrentWebView()->LoadURL(fStartPageURL);
755 break;
756
757 case CLEAR_HISTORY: {
758 BrowsingHistory* history = BrowsingHistory::DefaultInstance();
759 if (history->CountItems() == 0)
760 break;
761 BAlert* alert = new BAlert(B_TRANSLATE("Confirmation")BLocaleRoster::Default()->GetCatalog()->GetString(("Confirmation"
), "WebPositive Window")
,
762 B_TRANSLATE("Do you really want to "BLocaleRoster::Default()->GetCatalog()->GetString(("Do you really want to "
"clear the browsing history?"), "WebPositive Window")
763 "clear the browsing history?")BLocaleRoster::Default()->GetCatalog()->GetString(("Do you really want to "
"clear the browsing history?"), "WebPositive Window")
, B_TRANSLATE("Clear")BLocaleRoster::Default()->GetCatalog()->GetString(("Clear"
), "WebPositive Window")
,
764 B_TRANSLATE("Cancel")BLocaleRoster::Default()->GetCatalog()->GetString(("Cancel"
), "WebPositive Window")
);
765 alert->SetShortcut(1, B_ESCAPE);
766
767 if (alert->Go() == 0)
768 history->Clear();
769 break;
770 }
771
772 case CREATE_BOOKMARK:
773 _CreateBookmark();
774 break;
775 case SHOW_BOOKMARKS:
776 _ShowBookmarks();
777 break;
778
779 case B_REFS_RECEIVED:
780 {
781 // Currently the only source of these messages is the bookmarks menu.
782 // Filter refs into URLs, this also gets rid of refs for folders.
783 // For clicks on sub-folders in the bookmarks menu, we have Tracker
784 // open the corresponding folder.
785 entry_ref ref;
786 uint32 addedCount = 0;
787 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK((int)0); i++) {
788 BEntry entry(&ref);
789 uint32 addedSubCount = 0;
790 if (entry.IsDirectory()) {
791 BDirectory directory(&entry);
792 _AddBookmarkURLsRecursively(directory, message,
793 addedSubCount);
794 } else {
795 BFile file(&ref, B_READ_ONLY0x0000);
796 BString url;
797 if (_ReadURLAttr(file, url)) {
798 message->AddString("url", url.String());
799 addedSubCount++;
800 }
801 }
802 if (addedSubCount == 0) {
803 // Don't know what to do with this entry, just pass it
804 // on to the system to handle. Note that this may result
805 // in us opening other supported files via the application
806 // mechanism.
807 be_roster->Launch(&ref);
808 }
809 addedCount += addedSubCount;
810 }
811 message->RemoveName("refs");
812 if (addedCount > 10) {
813 BString string(B_TRANSLATE_COMMENT("Do you want to open %addedCount "BLocaleRoster::Default()->GetCatalog()->GetString(("Do you want to open %addedCount "
"bookmarks all at once?"), "WebPositive Window", ("Don't translate variable %addedCount."
))
814 "bookmarks all at once?", "Don't translate variable %addedCount.")BLocaleRoster::Default()->GetCatalog()->GetString(("Do you want to open %addedCount "
"bookmarks all at once?"), "WebPositive Window", ("Don't translate variable %addedCount."
))
);
815 string.ReplaceFirst("%addedCount", BString() << addedCount);
816
817 BAlert* alert = new BAlert(B_TRANSLATE("Open bookmarks confirmation")BLocaleRoster::Default()->GetCatalog()->GetString(("Open bookmarks confirmation"
), "WebPositive Window")
,
818 string.String(), B_TRANSLATE("Cancel")BLocaleRoster::Default()->GetCatalog()->GetString(("Cancel"
), "WebPositive Window")
, B_TRANSLATE("Open all")BLocaleRoster::Default()->GetCatalog()->GetString(("Open all"
), "WebPositive Window")
);
819 alert->SetShortcut(0, B_ESCAPE);
820 if (alert->Go() == 0)
821 break;
822 }
823 be_app->PostMessage(message);
824 break;
825 }
826 case B_SIMPLE_DATA:
827 {
828 // User possibly dropped files on this window.
829 // If there is more than one entry_ref, let the app handle it
830 // (open one new page per ref). If there is one ref, open it in
831 // this window.
832 type_code type;
833 int32 countFound;
834 if (message->GetInfo("refs", &type, &countFound) != B_OK((int)0)
835 || type != B_REF_TYPE) {
836 break;
837 }
838 if (countFound > 1) {
839 message->what = B_REFS_RECEIVED;
840 be_app->PostMessage(message);
841 break;
842 }
843 entry_ref ref;
844 if (message->FindRef("refs", &ref) != B_OK((int)0))
845 break;
846 BEntry entry(&ref, true);
847 BPath path;
848 if (!entry.Exists() || entry.GetPath(&path) != B_OK((int)0))
849 break;
850 CurrentWebView()->LoadURL(path.Path());
851 break;
852 }
853
854 case ZOOM_FACTOR_INCREASE:
855 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly);
856 break;
857 case ZOOM_FACTOR_DECREASE:
858 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly);
859 break;
860 case ZOOM_FACTOR_RESET:
861 CurrentWebView()->ResetZoomFactor();
862 break;
863 case ZOOM_TEXT_ONLY:
864 fZoomTextOnly = !fZoomTextOnly;
865 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly);
866 // TODO: Would be nice to have an instant update if the page is
867 // already zoomed.
868 break;
869
870 case TOGGLE_FULLSCREEN:
871 ToggleFullscreen();
872 break;
873
874 case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN:
875 _SetAutoHideInterfaceInFullscreen(
876 !fAutoHideInterfaceInFullscreenMode);
877 break;
878
879 case CHECK_AUTO_HIDE_INTERFACE:
880 _CheckAutoHideInterface();
881 break;
882
883 case SHOW_PAGE_SOURCE:
884 CurrentWebView()->WebPage()->SendPageSource();
885 break;
886 case B_PAGE_SOURCE_RESULT:
887 _HandlePageSourceResult(message);
888 break;
889
890 case EDIT_FIND_NEXT:
891 CurrentWebView()->FindString(fFindTextControl->Text(), true,
892 fFindCaseSensitiveCheckBox->Value());
893 break;
894 case FIND_TEXT_CHANGED:
895 {
896 bool findTextAvailable = strlen(fFindTextControl->Text()) > 0;
897 fFindPreviousMenuItem->SetEnabled(findTextAvailable);
898 fFindNextMenuItem->SetEnabled(findTextAvailable);
899 break;
900 }
901 case EDIT_FIND_PREVIOUS:
902 CurrentWebView()->FindString(fFindTextControl->Text(), false,
903 fFindCaseSensitiveCheckBox->Value());
904 break;
905 case EDIT_SHOW_FIND_GROUP:
906 if (!fFindGroup->IsVisible())
907 fFindGroup->SetVisible(true);
908 fFindTextControl->MakeFocus(true);
909 break;
910 case EDIT_HIDE_FIND_GROUP:
911 if (fFindGroup->IsVisible()) {
912 fFindGroup->SetVisible(false);
913 if (CurrentWebView() != NULL__null)
914 CurrentWebView()->MakeFocus(true);
915 }
916 break;
917
918 case B_CUT:
919 case B_COPY:
920 case B_PASTE:
921 {
922 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
923 if (textView != NULL__null)
924 textView->MessageReceived(message);
925 else if (CurrentWebView() != NULL__null)
926 CurrentWebView()->MessageReceived(message);
927 break;
928 }
929
930 case B_EDITING_CAPABILITIES_RESULT:
931 {
932 BWebView* webView;
933 if (message->FindPointer("view",
934 reinterpret_cast<void**>(&webView)) != B_OK((int)0)
935 || webView != CurrentWebView()) {
936 break;
937 }
938 bool canCut;
939 bool canCopy;
940 bool canPaste;
941 if (message->FindBool("can cut", &canCut) != B_OK((int)0))
942 canCut = false;
943 if (message->FindBool("can copy", &canCopy) != B_OK((int)0))
944 canCopy = false;
945 if (message->FindBool("can paste", &canPaste) != B_OK((int)0))
946 canPaste = false;
947 fCutMenuItem->SetEnabled(canCut);
948 fCopyMenuItem->SetEnabled(canCopy);
949 fPasteMenuItem->SetEnabled(canPaste);
950 break;
951 }
952
953 case SHOW_DOWNLOAD_WINDOW:
954 case SHOW_SETTINGS_WINDOW:
955 message->AddUInt32("workspaces", Workspaces());
956 be_app->PostMessage(message);
957 break;
958
959 case CLOSE_TAB:
960 if (fTabManager->CountTabs() > 1) {
961 int32 index;
962 if (message->FindInt32("tab index", &index) != B_OK((int)0))
963 index = fTabManager->SelectedTabIndex();
964 _ShutdownTab(index);
965 _UpdateTabGroupVisibility();
966 } else
967 PostMessage(B_QUIT_REQUESTED);
968 break;
969
970 case SELECT_TAB:
971 {
972 int32 index;
973 if (message->FindInt32("tab index", &index) == B_OK((int)0)
974 && fTabManager->SelectedTabIndex() != index
975 && fTabManager->CountTabs() > index) {
976 fTabManager->SelectTab(index);
977 }
978
979 break;
980 }
981
982 case TAB_CHANGED:
983 {
984 // This message may be received also when the last tab closed,
985 // i.e. with index == -1.
986 int32 index;
987 if (message->FindInt32("tab index", &index) != B_OK((int)0))
988 index = -1;
989 _TabChanged(index);
990 break;
991 }
992
993 case SETTINGS_VALUE_CHANGED:
994 {
995 BString name;
996 if (message->FindString("name", &name) != B_OK((int)0))
997 break;
998 bool flag;
999 BString string;
1000 uint32 value;
1001 if (name == kSettingsKeyShowTabsIfSinglePageOpen
1002 && message->FindBool("value", &flag) == B_OK((int)0)) {
1003 if (fShowTabsIfSinglePageOpen != flag) {
1004 fShowTabsIfSinglePageOpen = flag;
1005 _UpdateTabGroupVisibility();
1006 }
1007 } else if (name == kSettingsKeyAutoHidePointer
1008 && message->FindBool("value", &flag) == B_OK((int)0)) {
1009 fAutoHidePointer = flag;
1010 if (CurrentWebView())
1011 CurrentWebView()->SetAutoHidePointer(fAutoHidePointer);
1012 } else if (name == kSettingsKeyStartPageURL
1013 && message->FindString("value", &string) == B_OK((int)0)) {
1014 fStartPageURL = string;
1015 } else if (name == kSettingsKeySearchPageURL
1016 && message->FindString("value", &string) == B_OK((int)0)) {
1017 fSearchPageURL = string;
1018 } else if (name == kSettingsKeyNewWindowPolicy
1019 && message->FindUInt32("value", &value) == B_OK((int)0)) {
1020 fNewWindowPolicy = value;
1021 } else if (name == kSettingsKeyNewTabPolicy
1022 && message->FindUInt32("value", &value) == B_OK((int)0)) {
1023 fNewTabPolicy = value;
1024 } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode
1025 && message->FindBool("value", &flag) == B_OK((int)0)) {
1026 _SetAutoHideInterfaceInFullscreen(flag);
1027 } else if (name == kSettingsKeyShowHomeButton
1028 && message->FindBool("value", &flag) == B_OK((int)0)) {
1029 if (flag)
1030 fHomeButton->Show();
1031 else
1032 fHomeButton->Hide();
1033 }
1034 break;
1035 }
1036
1037 default:
1038 BWebWindow::MessageReceived(message);
1039 break;
1040 }
1041}
1042
1043
1044bool
1045BrowserWindow::QuitRequested()
1046{
1047 // TODO: Check for modified form data and ask user for confirmation, etc.
1048
1049 // Iterate over all tabs to delete all BWebViews.
1050 // Do this here, so WebKit tear down happens earlier.
1051 SetCurrentWebView(NULL__null);
1052 while (fTabManager->CountTabs() > 0)
1053 _ShutdownTab(0);
1054
1055 BMessage message(WINDOW_CLOSED);
1056 message.AddRect("window frame", WindowFrame());
1057 be_app->PostMessage(&message);
1058 return true;
1059}
1060
1061
1062void
1063BrowserWindow::MenusBeginning()
1064{
1065 _UpdateHistoryMenu();
1066 _UpdateClipboardItems();
1067 _ShowInterface(true);
1068}
1069
1070
1071void
1072BrowserWindow::ScreenChanged(BRect screenSize, color_space format)
1073{
1074 if (fIsFullscreen)
1075 _ResizeToScreen();
1076}
1077
1078
1079void
1080BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1081{
1082 if (fIsFullscreen)
1083 _ResizeToScreen();
1084}
1085
1086
1087static bool
1088viewIsChild(const BView* parent, const BView* view)
1089{
1090 if (parent == view)
1091 return true;
1092
1093 int32 count = parent->CountChildren();
1094 for (int32 i = 0; i < count; i++) {
1095 BView* child = parent->ChildAt(i);
1096 if (viewIsChild(child, view))
1097 return true;
1098 }
1099 return false;
1100}
1101
1102
1103void
1104BrowserWindow::SetCurrentWebView(BWebView* webView)
1105{
1106 if (webView == CurrentWebView())
1107 return;
1108
1109 if (CurrentWebView() != NULL__null) {
1110 // Remember the currently focused view before switching tabs,
1111 // so that we can revert the focus when switching back to this tab
1112 // later.
1113 PageUserData* userData = static_cast<PageUserData*>(
1114 CurrentWebView()->GetUserData());
1115 if (userData == NULL__null) {
1116 userData = new PageUserData(CurrentFocus());
1117 CurrentWebView()->SetUserData(userData);
1118 }
1119 userData->SetFocusedView(CurrentFocus());
1120 userData->SetURLInputContents(fURLInputGroup->Text());
1121 int32 selectionStart;
1122 int32 selectionEnd;
1123 fURLInputGroup->TextView()->GetSelection(&selectionStart,
1124 &selectionEnd);
1125 userData->SetURLInputSelection(selectionStart, selectionEnd);
1126 }
1127
1128 BWebWindow::SetCurrentWebView(webView);
1129
1130 if (webView != NULL__null) {
1131 webView->SetAutoHidePointer(fAutoHidePointer);
1132
1133 _UpdateTitle(webView->MainFrameTitle());
1134
1135 // Restore the previous focus or focus the web view.
1136 PageUserData* userData = static_cast<PageUserData*>(
1137 webView->GetUserData());
1138 BView* focusedView = NULL__null;
1139 if (userData != NULL__null)
1140 focusedView = userData->FocusedView();
1141
1142 if (focusedView != NULL__null
1143 && viewIsChild(GetLayout()->View(), focusedView)) {
1144 focusedView->MakeFocus(true);
1145 } else
1146 webView->MakeFocus(true);
1147
1148 if (userData != NULL__null) {
1149 fURLInputGroup->SetPageIcon(userData->PageIcon());
1150 if (userData->URLInputContents().Length())
1151 fURLInputGroup->SetText(userData->URLInputContents());
1152 else
1153 fURLInputGroup->SetText(webView->MainFrameURL());
1154 if (userData->URLInputSelectionStart() >= 0) {
1155 fURLInputGroup->TextView()->Select(
1156 userData->URLInputSelectionStart(),
1157 userData->URLInputSelectionEnd());
1158 }
1159 } else {
1160 fURLInputGroup->SetPageIcon(NULL__null);
1161 fURLInputGroup->SetText(webView->MainFrameURL());
1162 }
1163
1164 // Trigger update of the interface to the new page, by requesting
1165 // to resend all notifications.
1166 webView->WebPage()->ResendNotifications();
1167 } else
1168 _UpdateTitle("");
1169}
1170
1171
1172bool
1173BrowserWindow::IsBlankTab() const
1174{
1175 if (CurrentWebView() == NULL__null)
1176 return false;
1177 BString requestedURL = CurrentWebView()->MainFrameRequestedURL();
1178 return requestedURL.Length() == 0
1179 || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1);
1180}
1181
1182
1183void
1184BrowserWindow::CreateNewTab(const BString& _url, bool select, BWebView* webView)
1185{
1186 bool applyNewPagePolicy = webView == NULL__null;
1187 // Executed in app thread (new BWebPage needs to be created in app thread).
1188 if (webView == NULL__null)
1189 webView = new BWebView("web view");
1190
1191 bool isNewWindow = fTabManager->CountTabs() == 0;
1192
1193 fTabManager->AddTab(webView, B_TRANSLATE("New tab")BLocaleRoster::Default()->GetCatalog()->GetString(("New tab"
), "WebPositive Window")
);
1194
1195 BString url(_url);
1196 if (applyNewPagePolicy && url.Length() == 0)
1197 url = _NewTabURL(isNewWindow);
1198
1199 if (url.Length() > 0)
1200 webView->LoadURL(url.String());
1201
1202 if (select) {
1203 fTabManager->SelectTab(fTabManager->CountTabs() - 1);
1204 SetCurrentWebView(webView);
1205 webView->WebPage()->ResendNotifications();
1206 fURLInputGroup->SetPageIcon(NULL__null);
1207 fURLInputGroup->SetText(url.String());
1208 fURLInputGroup->MakeFocus(true);
1209 }
1210
1211 _ShowInterface(true);
1212 _UpdateTabGroupVisibility();
1213}
1214
1215
1216BRect
1217BrowserWindow::WindowFrame() const
1218{
1219 if (fIsFullscreen)
1220 return fNonFullscreenWindowFrame;
1221 else
1222 return Frame();
1223}
1224
1225
1226void
1227BrowserWindow::ToggleFullscreen()
1228{
1229 if (fIsFullscreen) {
1230 MoveTo(fNonFullscreenWindowFrame.LeftTop());
1231 ResizeTo(fNonFullscreenWindowFrame.Width(),
1232 fNonFullscreenWindowFrame.Height());
1233
1234 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
1235 SetLook(B_DOCUMENT_WINDOW_LOOK);
1236
1237 _ShowInterface(true);
1238 } else {
1239 fNonFullscreenWindowFrame = Frame();
1240 _ResizeToScreen();
1241
1242 SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE));
1243 SetLook(B_TITLED_WINDOW_LOOK);
1244 }
1245 fIsFullscreen = !fIsFullscreen;
1246 fFullscreenItem->SetMarked(fIsFullscreen);
1247 fToggleFullscreenButton->SetVisible(fIsFullscreen);
1248}
1249
1250
1251// #pragma mark - Notification API
1252
1253
1254void
1255BrowserWindow::NavigationRequested(const BString& url, BWebView* view)
1256{
1257}
1258
1259
1260void
1261BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction)
1262{
1263 // Always open new windows in the application thread, since
1264 // creating a BWebView will try to grab the application lock.
1265 // But our own WebPage may already try to lock us from within
1266 // the application thread -> dead-lock. Thus we can't wait for
1267 // a reply here.
1268 BMessage message(NEW_TAB);
1269 message.AddPointer("window", this);
1270 message.AddString("url", url);
1271 message.AddBool("select", primaryAction);
1272 be_app->PostMessage(&message);
1273}
1274
1275
1276void
1277BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame,
1278 bool modalDialog, bool resizable, bool activate)
1279{
1280 if (windowFrame.IsValid()) {
1281 BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings,
1282 BString(), INTERFACE_ELEMENT_STATUS, view);
1283 window->Show();
1284 } else
1285 CreateNewTab(BString(), activate, view);
1286}
1287
1288
1289void
1290BrowserWindow::CloseWindowRequested(BWebView* view)
1291{
1292 int32 index = fTabManager->TabForView(view);
1293 if (index < 0) {
1294 // Tab is already gone.
1295 return;
1296 }
1297 BMessage message(CLOSE_TAB);
1298 message.AddInt32("tab index", index);
1299 PostMessage(&message, this);
1300}
1301
1302
1303void
1304BrowserWindow::LoadNegotiating(const BString& url, BWebView* view)
1305{
1306 if (view != CurrentWebView())
1307 return;
1308
1309 fURLInputGroup->SetText(url.String());
1310
1311 BString status(B_TRANSLATE("Requesting %url")BLocaleRoster::Default()->GetCatalog()->GetString(("Requesting %url"
), "WebPositive Window")
);
1312 status.ReplaceFirst("%url", url);
1313 view->WebPage()->SetStatusMessage(status);
1314}
1315
1316
1317void
1318BrowserWindow::LoadCommitted(const BString& url, BWebView* view)
1319{
1320 if (view != CurrentWebView())
1321 return;
1322
1323 // This hook is invoked when the load is commited.
1324 fURLInputGroup->SetText(url.String());
1325
1326 BString status(B_TRANSLATE("Loading %url")BLocaleRoster::Default()->GetCatalog()->GetString(("Loading %url"
), "WebPositive Window")
);
1327 status.ReplaceFirst("%url", url);
1328 view->WebPage()->SetStatusMessage(status);
1329}
1330
1331
1332void
1333BrowserWindow::LoadProgress(float progress, BWebView* view)
1334{
1335 if (view != CurrentWebView())
1336 return;
1337
1338 if (progress < 100 && fLoadingProgressBar->IsHidden())
1339 _ShowProgressBar(true);
1340 else if (progress == 100 && !fLoadingProgressBar->IsHidden())
1341 _ShowProgressBar(false);
1342 fLoadingProgressBar->SetTo(progress);
1343}
1344
1345
1346void
1347BrowserWindow::LoadFailed(const BString& url, BWebView* view)
1348{
1349 if (view != CurrentWebView())
1350 return;
1351
1352 BString status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. "BLocaleRoster::Default()->GetCatalog()->GetString(("%url failed"
), "WebPositive Window", ("Loading URL failed. " "Don't translate variable %url."
))
1353 "Don't translate variable %url.")BLocaleRoster::Default()->GetCatalog()->GetString(("%url failed"
), "WebPositive Window", ("Loading URL failed. " "Don't translate variable %url."
))
);
1354 status.ReplaceFirst("%url", url);
1355 view->WebPage()->SetStatusMessage(status);
1356 if (!fLoadingProgressBar->IsHidden())
1357 fLoadingProgressBar->Hide();
1358}
1359
1360
1361void
1362BrowserWindow::LoadFinished(const BString& url, BWebView* view)
1363{
1364 if (view != CurrentWebView())
1365 return;
1366
1367 BString status(B_TRANSLATE_COMMENT("%url finished", "Loading URL "BLocaleRoster::Default()->GetCatalog()->GetString(("%url finished"
), "WebPositive Window", ("Loading URL " "finished. Don't translate variable %url."
))
1368 "finished. Don't translate variable %url.")BLocaleRoster::Default()->GetCatalog()->GetString(("%url finished"
), "WebPositive Window", ("Loading URL " "finished. Don't translate variable %url."
))
);
1369 status.ReplaceFirst("%url", url);
1370 view->WebPage()->SetStatusMessage(status);
1371 if (!fLoadingProgressBar->IsHidden())
1372 fLoadingProgressBar->Hide();
1373
1374 NavigationCapabilitiesChanged(fBackButton->IsEnabled(),
1375 fForwardButton->IsEnabled(), false, view);
1376
1377 int32 tabIndex = fTabManager->TabForView(view);
1378 if (tabIndex > 0 && strcmp(B_TRANSLATE("New tab")BLocaleRoster::Default()->GetCatalog()->GetString(("New tab"
), "WebPositive Window")
,
1379 fTabManager->TabLabel(tabIndex)) == 0)
1380 fTabManager->SetTabLabel(tabIndex, url);
1381}
1382
1383
1384void
1385BrowserWindow::MainDocumentError(const BString& failingURL,
1386 const BString& localizedDescription, BWebView* view)
1387{
1388 // Make sure we show the page that contains the view.
1389 if (!_ShowPage(view))
1390 return;
1391
1392 BWebWindow::MainDocumentError(failingURL, localizedDescription, view);
1393
1394 // TODO: Remove the failing URL from the BrowsingHistory!
1395}
1396
1397
1398void
1399BrowserWindow::TitleChanged(const BString& title, BWebView* view)
1400{
1401 int32 tabIndex = fTabManager->TabForView(view);
1402 if (tabIndex < 0)
1403 return;
1404
1405 fTabManager->SetTabLabel(tabIndex, title);
1406
1407 if (view != CurrentWebView())
1408 return;
1409
1410 _UpdateTitle(title);
1411}
1412
1413
1414void
1415BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view)
1416{
1417 // The view may already be gone, since this notification arrives
1418 // asynchronously.
1419 if (!fTabManager->HasView(view))
1420 return;
1421
1422 _SetPageIcon(view, icon);
1423}
1424
1425
1426void
1427BrowserWindow::ResizeRequested(float width, float height, BWebView* view)
1428{
1429 if (view != CurrentWebView())
1430 return;
1431
1432 // Ignore request when there is more than one BWebView embedded.
1433 if (fTabManager->CountTabs() > 1)
1434 return;
1435
1436 // Make sure the new frame is not larger than the screen frame minus
1437 // window decorator border.
1438 BScreen screen(this);
1439 BRect screenFrame = screen.Frame();
1440 BRect decoratorFrame = DecoratorFrame();
1441 BRect frame = Frame();
1442
1443 screenFrame.left += decoratorFrame.left - frame.left;
1444 screenFrame.right += decoratorFrame.right - frame.right;
1445 screenFrame.top += decoratorFrame.top - frame.top;
1446 screenFrame.bottom += decoratorFrame.bottom - frame.bottom;
1447
1448 width = min_c(width, screen.Frame().Width())((width)>(screen.Frame().Width())?(screen.Frame().Width())
:(width))
;
1449 height = min_c(height, screen.Frame().Height())((height)>(screen.Frame().Height())?(screen.Frame().Height
()):(height))
;
1450
1451 frame.right = frame.left + width;
1452 frame.bottom = frame.top + height;
1453
1454 // frame is now not larger than screenFrame, but may still be partly outside
1455 if (!screenFrame.Contains(frame)) {
1456 if (frame.left < screenFrame.left)
1457 frame.OffsetBy(screenFrame.left - frame.left, 0);
1458 else if (frame.right > screenFrame.right)
1459 frame.OffsetBy(screenFrame.right - frame.right, 0);
1460 if (frame.top < screenFrame.top)
1461 frame.OffsetBy(screenFrame.top - frame.top, 0);
1462 else if (frame.bottom > screenFrame.bottom)
1463 frame.OffsetBy(screenFrame.bottom - frame.bottom, 0);
1464 }
1465
1466 MoveTo(frame.left, frame.top);
1467 ResizeTo(width, height);
1468}
1469
1470
1471void
1472BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view)
1473{
1474 // TODO
1475 // TODO: Ignore request when there is more than one BWebView embedded!
1476}
1477
1478
1479void
1480BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view)
1481{
1482 // TODO
1483 // TODO: Ignore request when there is more than one BWebView embedded!
1484}
1485
1486
1487void
1488BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view)
1489{
1490 // TODO
1491 // TODO: Ignore request when there is more than one BWebView embedded!
1492}
1493
1494
1495void
1496BrowserWindow::SetResizable(bool flag, BWebView* view)
1497{
1498 // TODO: Ignore request when there is more than one BWebView embedded!
1499
1500 if (flag)
1501 SetFlags(Flags() & ~B_NOT_RESIZABLE);
1502 else
1503 SetFlags(Flags() | B_NOT_RESIZABLE);
1504}
1505
1506
1507void
1508BrowserWindow::StatusChanged(const BString& statusText, BWebView* view)
1509{
1510 if (view != CurrentWebView())
1511 return;
1512
1513 if (fStatusText)
1514 fStatusText->SetText(statusText.String());
1515}
1516
1517
1518void
1519BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward,
1520 bool canGoForward, bool canStop, BWebView* view)
1521{
1522 if (view != CurrentWebView())
1523 return;
1524
1525 fBackButton->SetEnabled(canGoBackward);
1526 fForwardButton->SetEnabled(canGoForward);
1527 fStopButton->SetEnabled(canStop);
1528
1529 fBackMenuItem->SetEnabled(canGoBackward);
1530 fForwardMenuItem->SetEnabled(canGoForward);
1531}
1532
1533
1534void
1535BrowserWindow::UpdateGlobalHistory(const BString& url)
1536{
1537 BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url));
1538}
1539
1540
1541bool
1542BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser,
1543 BString& inOutPassword, bool& inOutRememberCredentials,
1544 uint32 failureCount, BWebView* view)
1545{
1546 CredentialsStorage* persistentStorage
1547 = CredentialsStorage::PersistentInstance();
1548 CredentialsStorage* sessionStorage
1549 = CredentialsStorage::SessionInstance();
1550
1551 // TODO: Using the message as key here is not so smart.
1552 HashKeyString key(message);
1553
1554 if (failureCount == 0) {
1555 if (persistentStorage->Contains(key)) {
1556 Credentials credentials = persistentStorage->GetCredentials(key);
1557 inOutUser = credentials.Username();
1558 inOutPassword = credentials.Password();
1559 return true;
1560 } else if (sessionStorage->Contains(key)) {
1561 Credentials credentials = sessionStorage->GetCredentials(key);
1562 inOutUser = credentials.Username();
1563 inOutPassword = credentials.Password();
1564 return true;
1565 }
1566 }
1567 // Switch to the page for which this authentication is required.
1568 if (!_ShowPage(view))
1569 return false;
1570
1571 AuthenticationPanel* panel = new AuthenticationPanel(Frame());
1572 // Panel auto-destructs.
1573 bool success = panel->getAuthentication(message, inOutUser, inOutPassword,
1574 inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword,
1575 &inOutRememberCredentials);
1576 if (success) {
1577 Credentials credentials(inOutUser, inOutPassword);
1578 if (inOutRememberCredentials)
1579 persistentStorage->PutCredentials(key, credentials);
1580 else
1581 sessionStorage->PutCredentials(key, credentials);
1582 }
1583 return success;
1584}
1585
1586
1587// #pragma mark - private
1588
1589
1590void
1591BrowserWindow::_UpdateTitle(const BString& title)
1592{
1593 BString windowTitle = title;
1594 if (windowTitle.Length() > 0)
1595 windowTitle << " - ";
1596 windowTitle << kApplicationName;
1597 SetTitle(windowTitle.String());
1598}
1599
1600
1601void
1602BrowserWindow::_UpdateTabGroupVisibility()
1603{
1604 if (Lock()) {
1605 if (fInterfaceVisible)
1606 fTabGroup->SetVisible(_TabGroupShouldBeVisible());
1607 fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1);
1608 Unlock();
1609 }
1610}
1611
1612
1613bool
1614BrowserWindow::_TabGroupShouldBeVisible() const
1615{
1616 return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1)
1617 && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0;
1618}
1619
1620
1621void
1622BrowserWindow::_ShutdownTab(int32 index)
1623{
1624 BView* view = fTabManager->RemoveTab(index);
1625 BWebView* webView = dynamic_cast<BWebView*>(view);
1626 if (webView == CurrentWebView())
1627 SetCurrentWebView(NULL__null);
1628 if (webView != NULL__null)
1629 webView->Shutdown();
1630 else
1631 delete view;
1632}
1633
1634
1635void
1636BrowserWindow::_TabChanged(int32 index)
1637{
1638 SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index)));
1639}
1640
1641
1642status_t
1643BrowserWindow::_BookmarkPath(BPath& path) const
1644{
1645 status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1646 if (ret != B_OK((int)0))
1647 return ret;
1648
1649 ret = path.Append(kApplicationName);
1650 if (ret != B_OK((int)0))
1651 return ret;
1652
1653 ret = path.Append("Bookmarks");
1654 if (ret != B_OK((int)0))
1655 return ret;
1656
1657 return create_directory(path.Path(), 0777);
1658}
1659
1660
1661void
1662BrowserWindow::_CreateBookmark()
1663{
1664 BPath path;
1665 status_t status = _BookmarkPath(path);
1666 if (status != B_OK((int)0)) {
1667 BString message(B_TRANSLATE_COMMENT("There was an error retrieving "BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error retrieving "
"the bookmark folder.\n\nError: %error"), "WebPositive Window"
, ("Don't translate the " "variable %error"))
1668 "the bookmark folder.\n\nError: %error", "Don't translate the "BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error retrieving "
"the bookmark folder.\n\nError: %error"), "WebPositive Window"
, ("Don't translate the " "variable %error"))
1669 "variable %error")BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error retrieving "
"the bookmark folder.\n\nError: %error"), "WebPositive Window"
, ("Don't translate the " "variable %error"))
);
1670 message.ReplaceFirst("%error", strerror(status));
1671 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error")BLocaleRoster::Default()->GetCatalog()->GetString(("Bookmark error"
), "WebPositive Window")
,
1672 message.String(), B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK"
), "WebPositive Window")
, NULL__null, NULL__null,
1673 B_WIDTH_AS_USUAL, B_STOP_ALERT);
1674 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1675 alert->Go();
1676 return;
1677 }
1678 BWebView* webView = CurrentWebView();
1679 BString url(webView->MainFrameURL());
1680 // Create a bookmark file
1681 BFile bookmarkFile;
1682 BString bookmarkName(webView->MainFrameTitle());
1683 if (bookmarkName.Length() == 0) {
1684 bookmarkName = url;
1685 int32 leafPos = bookmarkName.FindLast('/');
1686 if (leafPos >= 0)
1687 bookmarkName.Remove(0, leafPos + 1);
1688 }
1689 // Make sure the bookmark title does not contain chars that are not
1690 // allowed in file names.
1691 bookmarkName.ReplaceAll('/', '-');
1692
1693 // Check that the bookmark exists nowhere in the bookmark hierarchy,
1694 // though the intended file name must match, we don't search the stored
1695 // URLs, only for matching file names.
1696 BDirectory directory(path.Path());
1697 if (status == B_OK((int)0) && _CheckBookmarkExists(directory, bookmarkName, url)) {
1698 BString message(B_TRANSLATE_COMMENT("A bookmark for this page "BLocaleRoster::Default()->GetCatalog()->GetString(("A bookmark for this page "
"(%bookmarkName) already exists."), "WebPositive Window", ("Don't translate variable "
"%bookmarkName"))
1699 "(%bookmarkName) already exists.", "Don't translate variable "BLocaleRoster::Default()->GetCatalog()->GetString(("A bookmark for this page "
"(%bookmarkName) already exists."), "WebPositive Window", ("Don't translate variable "
"%bookmarkName"))
1700 "%bookmarkName")BLocaleRoster::Default()->GetCatalog()->GetString(("A bookmark for this page "
"(%bookmarkName) already exists."), "WebPositive Window", ("Don't translate variable "
"%bookmarkName"))
);
1701 message.ReplaceFirst("%bookmarkName", bookmarkName);
1702 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark info")BLocaleRoster::Default()->GetCatalog()->GetString(("Bookmark info"
), "WebPositive Window")
,
1703 message.String(), B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK"
), "WebPositive Window")
);
1704 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1705 alert->Go();
1706 return;
1707 }
1708
1709 BPath entryPath(path);
1710 status = entryPath.Append(bookmarkName);
1711 BEntry entry;
1712 if (status == B_OK((int)0))
1713 status = entry.SetTo(entryPath.Path(), true);
1714 if (status == B_OK((int)0)) {
1715 int32 tries = 1;
1716 while (entry.Exists()) {
1717 // Find a unique name for the bookmark, there may still be a
1718 // file in the way that stores a different URL.
1719 bookmarkName = webView->MainFrameTitle();
1720 bookmarkName << " " << tries++;
1721 entryPath = path;
1722 status = entryPath.Append(bookmarkName);
1723 if (status == B_OK((int)0))
1724 status = entry.SetTo(entryPath.Path(), true);
1725 if (status != B_OK((int)0))
1726 break;
1727 }
1728 }
1729 if (status == B_OK((int)0)) {
1730 status = bookmarkFile.SetTo(&entry,
1731 B_CREATE_FILE0x0200 | B_ERASE_FILE0x0400 | B_WRITE_ONLY0x0001);
1732 }
1733
1734 // Write bookmark meta data
1735 if (status == B_OK((int)0))
1736 status = bookmarkFile.WriteAttrString("META:url", &url);
1737 if (status == B_OK((int)0)) {
1738 BString title = webView->MainFrameTitle();
1739 bookmarkFile.WriteAttrString("META:title", &title);
1740 }
1741
1742 BNodeInfo nodeInfo(&bookmarkFile);
1743 if (status == B_OK((int)0)) {
1744 status = nodeInfo.SetType("application/x-vnd.Be-bookmark");
1745 if (status == B_OK((int)0)) {
1746 PageUserData* userData = static_cast<PageUserData*>(
1747 webView->GetUserData());
1748 if (userData != NULL__null && userData->PageIcon() != NULL__null) {
1749 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK,
1750 B_CMAP8);
1751 status_t ret = miniIcon.ImportBits(userData->PageIcon());
1752 if (ret == B_OK((int)0))
1753 ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON);
1754 if (ret != B_OK((int)0)) {
1755 fprintf(stderr, "Failed to store mini icon for bookmark: "
1756 "%s\n", strerror(ret));
1757 }
1758 BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK,
1759 B_CMAP8);
1760 // TODO: Store 32x32 favicon which is often provided by sites.
1761 const uint8* src = (const uint8*)miniIcon.Bits();
1762 uint32 srcBPR = miniIcon.BytesPerRow();
1763 uint8* dst = (uint8*)largeIcon.Bits();
1764 uint32 dstBPR = largeIcon.BytesPerRow();
1765 for (uint32 y = 0; y < 16; y++) {
1766 const uint8* s = src;
1767 uint8* d = dst;
1768 for (uint32 x = 0; x < 16; x++) {
1769 *d++ = *s;
1770 *d++ = *s++;
1771 }
1772 dst += dstBPR;
1773 s = src;
1774 for (uint32 x = 0; x < 16; x++) {
1775 *d++ = *s;
1776 *d++ = *s++;
1777 }
1778 dst += dstBPR;
1779 src += srcBPR;
1780 }
1781 if (ret == B_OK((int)0))
1782 ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON);
1783 if (ret != B_OK((int)0)) {
1784 fprintf(stderr, "Failed to store large icon for bookmark: "
1785 "%s\n", strerror(ret));
1786 }
1787 }
1788 }
1789 }
1790
1791 if (status != B_OK((int)0)) {
1792 BString message(B_TRANSLATE_COMMENT("There was an error creating the "BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error creating the "
"bookmark file.\n\nError: %error"), "WebPositive Window", ("Don't translate variable "
"%error"))
1793 "bookmark file.\n\nError: %error", "Don't translate variable "BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error creating the "
"bookmark file.\n\nError: %error"), "WebPositive Window", ("Don't translate variable "
"%error"))
1794 "%error")BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error creating the "
"bookmark file.\n\nError: %error"), "WebPositive Window", ("Don't translate variable "
"%error"))
);
1795 message.ReplaceFirst("%error", strerror(status));
1796 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error")BLocaleRoster::Default()->GetCatalog()->GetString(("Bookmark error"
), "WebPositive Window")
,
1797 message.String(), B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK"
), "WebPositive Window")
, NULL__null, NULL__null,
1798 B_WIDTH_AS_USUAL, B_STOP_ALERT);
1799 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1800 alert->Go();
1801 return;
1802 }
1803}
1804
1805
1806void
1807BrowserWindow::_ShowBookmarks()
1808{
1809 BPath path;
1810 entry_ref ref;
1811 status_t status = _BookmarkPath(path);
1812 if (status == B_OK((int)0))
1813 status = get_ref_for_path(path.Path(), &ref);
1814 if (status == B_OK((int)0))
1815 status = be_roster->Launch(&ref);
1816
1817 if (status != B_OK((int)0) && status != B_ALREADY_RUNNING(((-2147483647 - 1) + 0x2000) + 4)) {
1818 BString message(B_TRANSLATE_COMMENT("There was an error trying to "BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error trying to "
"show the Bookmarks folder.\n\nError: %error"), "WebPositive Window"
, ("Don't translate variable " "%error"))
1819 "show the Bookmarks folder.\n\nError: %error", "Don't translate variable "BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error trying to "
"show the Bookmarks folder.\n\nError: %error"), "WebPositive Window"
, ("Don't translate variable " "%error"))
1820 "%error")BLocaleRoster::Default()->GetCatalog()->GetString(("There was an error trying to "
"show the Bookmarks folder.\n\nError: %error"), "WebPositive Window"
, ("Don't translate variable " "%error"))
);
1821 message.ReplaceFirst("%error", strerror(status));
1822 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error")BLocaleRoster::Default()->GetCatalog()->GetString(("Bookmark error"
), "WebPositive Window")
,
1823 message.String(), B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK"
), "WebPositive Window")
, NULL__null, NULL__null,
1824 B_WIDTH_AS_USUAL, B_STOP_ALERT);
1825 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1826 alert->Go();
1827 return;
1828 }
1829}
1830
1831
1832bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory,
1833 const BString& bookmarkName, const BString& url) const
1834{
1835 BEntry entry;
1836 while (directory.GetNextEntry(&entry) == B_OK((int)0)) {
1837 if (entry.IsDirectory()) {
1838 BDirectory subBirectory(&entry);
1839 // At least preserve the entry file handle when recursing into
1840 // sub-folders... eventually we will run out, though, with very
1841 // deep hierarchy.
1842 entry.Unset();
1843 if (_CheckBookmarkExists(subBirectory, bookmarkName, url))
1844 return true;
1845 } else {
1846 char entryName[B_FILE_NAME_LENGTH(256)];
1847 if (entry.GetName(entryName) != B_OK((int)0) || bookmarkName != entryName)
1848 continue;
1849 BString storedURL;
1850 BFile file(&entry, B_READ_ONLY0x0000);
1851 if (_ReadURLAttr(file, storedURL)) {
1852 // Just bail if the bookmark already exists
1853 if (storedURL == url)
1854 return true;
1855 }
1856 }
1857 }
1858 return false;
1859}
1860
1861
1862bool
1863BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const
1864{
1865 return bookmarkFile.InitCheck() == B_OK((int)0)
1866 && bookmarkFile.ReadAttrString("META:url", &url) == B_OK((int)0);
1867}
1868
1869
1870void
1871BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory,
1872 BMessage* message, uint32& addedCount) const
1873{
1874 BEntry entry;
1875 while (directory.GetNextEntry(&entry) == B_OK((int)0)) {
1876 if (entry.IsDirectory()) {
1877 BDirectory subBirectory(&entry);
1878 // At least preserve the entry file handle when recursing into
1879 // sub-folders... eventually we will run out, though, with very
1880 // deep hierarchy.
1881 entry.Unset();
1882 _AddBookmarkURLsRecursively(subBirectory, message, addedCount);
1883 } else {
1884 BString storedURL;
1885 BFile file(&entry, B_READ_ONLY0x0000);
1886 if (_ReadURLAttr(file, storedURL)) {
1887 message->AddString("url", storedURL.String());
1888 addedCount++;
1889 }
1890 }
1891 }
1892}
1893
1894
1895void
1896BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon)
1897{
1898 PageUserData* userData = static_cast<PageUserData*>(view->GetUserData());
1899 if (userData == NULL__null) {
1900 userData = new(std::nothrow) PageUserData(NULL__null);
1901 if (userData == NULL__null)
1902 return;
1903 view->SetUserData(userData);
1904 }
1905 // The PageUserData makes a copy of the icon, which we pass on to
1906 // the TabManager for display in the respective tab.
1907 userData->SetPageIcon(icon);
1908 fTabManager->SetTabIcon(view, userData->PageIcon());
1909 if (view == CurrentWebView())
1910 fURLInputGroup->SetPageIcon(icon);
1911}
1912
1913
1914static void
1915addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem)
1916{
1917 BString baseURLLabel = baseURL(BString(newItem->Label()));
1918 for (int32 i = menu->CountItems() - 1; i >= 0; i--) {
1919 BMenuItem* item = menu->ItemAt(i);
1920 BString label = item->Label();
1921 if (label.FindFirst(baseURLLabel) >= 0) {
1922 if (item->Submenu()) {
1923 // Submenu was already added in previous iteration.
1924 item->Submenu()->AddItem(newItem);
1925 return;
1926 } else {
1927 menu->RemoveItem(item);
1928 BMenu* subMenu = new BMenu(baseURLLabel.String());
1929 subMenu->AddItem(item);
1930 subMenu->AddItem(newItem);
1931 // Add common submenu for this base URL, clickable.
1932 BMessage* message = new BMessage(GOTO_URL);
1933 message->AddString("url", baseURLLabel.String());
1934 menu->AddItem(new BMenuItem(subMenu, message), i);
1935 return;
1936 }
1937 }
1938 }
1939 menu->AddItem(newItem);
1940}
1941
1942
1943static void
1944addOrDeleteMenu(BMenu* menu, BMenu* toMenu)
1945{
1946 if (menu->CountItems() > 0)
1947 toMenu->AddItem(menu);
1948 else
1949 delete menu;
1950}
1951
1952
1953void
1954BrowserWindow::_UpdateHistoryMenu()
1955{
1956 BMenuItem* menuItem;
1957 while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount)))
1
Loop condition is false. Execution continues on line 1960
1958 delete menuItem;
1959
1960 BrowsingHistory* history = BrowsingHistory::DefaultInstance();
1961 if (!history->Lock())
2
Taking false branch
1962 return;
1963
1964 int32 count = history->CountItems();
1965 BMenuItem* clearHistoryItem = new BMenuItem(B_TRANSLATE("Clear history")BLocaleRoster::Default()->GetCatalog()->GetString(("Clear history"
), "WebPositive Window")
,
1966 new BMessage(CLEAR_HISTORY));
1967 clearHistoryItem->SetEnabled(count > 0);
3
Assuming 'count' is <= 0
1968 fHistoryMenu->AddItem(clearHistoryItem);
1969 if (count == 0) {
4
Assuming 'count' is not equal to 0
5
Taking false branch
1970 history->Unlock();
1971 return;
1972 }
1973 fHistoryMenu->AddSeparatorItem();
1974
1975 BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME);
1976 todayStart.SetTime(BTime(0, 0, 0));
1977
1978 BDateTime oneDayAgoStart = todayStart;
1979 oneDayAgoStart.Date().AddDays(-1);
1980
1981 BDateTime twoDaysAgoStart = oneDayAgoStart;
1982 twoDaysAgoStart.Date().AddDays(-1);
1983
1984 BDateTime threeDaysAgoStart = twoDaysAgoStart;
1985 threeDaysAgoStart.Date().AddDays(-1);
1986
1987 BDateTime fourDaysAgoStart = threeDaysAgoStart;
1988 fourDaysAgoStart.Date().AddDays(-1);
1989
1990 BDateTime fiveDaysAgoStart = fourDaysAgoStart;
1991 fiveDaysAgoStart.Date().AddDays(-1);
1992
1993 BMenu* todayMenu = new BMenu(B_TRANSLATE("Today")BLocaleRoster::Default()->GetCatalog()->GetString(("Today"
), "WebPositive Window")
);
1994 BMenu* yesterdayMenu = new BMenu(B_TRANSLATE("Yesterday")BLocaleRoster::Default()->GetCatalog()->GetString(("Yesterday"
), "WebPositive Window")
);
1995 BMenu* twoDaysAgoMenu = new BMenu(
1996 twoDaysAgoStart.Date().LongDayName().String());
1997 BMenu* threeDaysAgoMenu = new BMenu(
6
Memory is allocated
1998 threeDaysAgoStart.Date().LongDayName().String());
1999 BMenu* fourDaysAgoMenu = new BMenu(
2000 fourDaysAgoStart.Date().LongDayName().String());
2001 BMenu* fiveDaysAgoMenu = new BMenu(
2002 fiveDaysAgoStart.Date().LongDayName().String());
2003 BMenu* earlierMenu = new BMenu(B_TRANSLATE("Earlier")BLocaleRoster::Default()->GetCatalog()->GetString(("Earlier"
), "WebPositive Window")
);
2004
2005 for (int32 i = 0; i < count; i++) {
7
Loop condition is false. Execution continues on line 2029
2006 BrowsingHistoryItem historyItem = history->HistoryItemAt(i);
2007 BMessage* message = new BMessage(GOTO_URL);
2008 message->AddString("url", historyItem.URL().String());
2009
2010 BString truncatedUrl(historyItem.URL());
2011 be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480);
2012 menuItem = new BMenuItem(truncatedUrl, message);
2013
2014 if (historyItem.DateTime() < fiveDaysAgoStart)
2015 addItemToMenuOrSubmenu(earlierMenu, menuItem);
2016 else if (historyItem.DateTime() < fourDaysAgoStart)
2017 addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem);
2018 else if (historyItem.DateTime() < threeDaysAgoStart)
2019 addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem);
2020 else if (historyItem.DateTime() < twoDaysAgoStart)
2021 addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem);
2022 else if (historyItem.DateTime() < oneDayAgoStart)
2023 addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem);
2024 else if (historyItem.DateTime() < todayStart)
2025 addItemToMenuOrSubmenu(yesterdayMenu, menuItem);
2026 else
2027 addItemToMenuOrSubmenu(todayMenu, menuItem);
2028 }
2029 history->Unlock();
8
Potential leak of memory pointed to by 'threeDaysAgoMenu'
2030
2031 addOrDeleteMenu(todayMenu, fHistoryMenu);
2032 addOrDeleteMenu(yesterdayMenu, fHistoryMenu);
2033 addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu);
2034 addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu);
2035 addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu);
2036 addOrDeleteMenu(earlierMenu, fHistoryMenu);
2037}
2038
2039
2040void
2041BrowserWindow::_UpdateClipboardItems()
2042{
2043 BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus());
2044 if (focusTextView != NULL__null) {
2045 int32 selectionStart;
2046 int32 selectionEnd;
2047 focusTextView->GetSelection(&selectionStart, &selectionEnd);
2048 bool hasSelection = selectionStart < selectionEnd;
2049 bool canPaste = false;
2050 // A BTextView has the focus.
2051 if (be_clipboard->Lock()) {
2052 BMessage* data = be_clipboard->Data();
2053 if (data != NULL__null)
2054 canPaste = data->HasData("text/plain", B_MIME_TYPE);
2055 be_clipboard->Unlock();
2056 }
2057 fCutMenuItem->SetEnabled(hasSelection);
2058 fCopyMenuItem->SetEnabled(hasSelection);
2059 fPasteMenuItem->SetEnabled(canPaste);
2060 } else if (CurrentWebView() != NULL__null) {
2061 // Trigger update of the clipboard items, even if the
2062 // BWebView doesn't have focus, we'll dispatch these message
2063 // there anyway. This works so fast that the user can never see
2064 // the wrong enabled state when the menu opens until the result
2065 // message arrives. The initial state needs to be enabled, since
2066 // standard shortcut handling is always wrapped inside MenusBeginning()
2067 // and MenusEnded(), and since we update items asynchronously, we need
2068 // to have them enabled to begin with.
2069 fCutMenuItem->SetEnabled(true);
2070 fCopyMenuItem->SetEnabled(true);
2071 fPasteMenuItem->SetEnabled(true);
2072
2073 CurrentWebView()->WebPage()->SendEditingCapabilities();
2074 }
2075}
2076
2077
2078bool
2079BrowserWindow::_ShowPage(BWebView* view)
2080{
2081 if (view != CurrentWebView()) {
2082 int32 tabIndex = fTabManager->TabForView(view);
2083 if (tabIndex < 0) {
2084 // Page seems to be gone already?
2085 return false;
2086 }
2087 fTabManager->SelectTab(tabIndex);
2088 _TabChanged(tabIndex);
2089 UpdateIfNeeded();
2090 }
2091 return true;
2092}
2093
2094
2095void
2096BrowserWindow::_ResizeToScreen()
2097{
2098 BScreen screen(this);
2099 MoveTo(0, 0);
2100 ResizeTo(screen.Frame().Width(), screen.Frame().Height());
2101}
2102
2103
2104void
2105BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt)
2106{
2107 if (fAutoHideInterfaceInFullscreenMode == doIt)
2108 return;
2109
2110 fAutoHideInterfaceInFullscreenMode = doIt;
2111 if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
2112 doIt) != doIt) {
2113 fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
2114 doIt);
2115 }
2116
2117 if (fAutoHideInterfaceInFullscreenMode) {
2118 BMessage message(CHECK_AUTO_HIDE_INTERFACE);
2119 fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000);
2120 } else {
2121 delete fPulseRunner;
2122 fPulseRunner = NULL__null;
2123 _ShowInterface(true);
2124 }
2125}
2126
2127
2128void
2129BrowserWindow::_CheckAutoHideInterface()
2130{
2131 if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode
2132 || (CurrentWebView() != NULL__null && !CurrentWebView()->IsFocus())) {
2133 return;
2134 }
2135
2136 if (fLastMousePos.y == 0)
2137 _ShowInterface(true);
2138 else if (fNavigationGroup->IsVisible()
2139 && fLastMousePos.y > fNavigationGroup->Frame().bottom
2140 && system_time() - fLastMouseMovedTime > 1000000) {
2141 // NOTE: Do not re-use navigationGroupBottom in the above
2142 // check, since we only want to hide the interface when it is visible.
2143 _ShowInterface(false);
2144 }
2145}
2146
2147
2148void
2149BrowserWindow::_ShowInterface(bool show)
2150{
2151 if (fInterfaceVisible == show)
2152 return;
2153
2154 fInterfaceVisible = show;
2155
2156 if (show) {
2157#if !INTEGRATE_MENU_INTO_TAB_BAR0
2158 fMenuGroup->SetVisible(
2159 (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0);
2160#endif
2161 fTabGroup->SetVisible(_TabGroupShouldBeVisible());
2162 fNavigationGroup->SetVisible(
2163 (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0);
2164 fStatusGroup->SetVisible(
2165 (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0);
2166 } else {
2167 fMenuGroup->SetVisible(false);
2168 fTabGroup->SetVisible(false);
2169 fNavigationGroup->SetVisible(false);
2170 fStatusGroup->SetVisible(false);
2171 }
2172 // TODO: Setting the group visible seems to unhide the status bar.
2173 // Fix in Haiku?
2174 while (!fLoadingProgressBar->IsHidden())
2175 fLoadingProgressBar->Hide();
2176}
2177
2178
2179void
2180BrowserWindow::_ShowProgressBar(bool show)
2181{
2182 if (show) {
2183 if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements
2184 & INTERFACE_ELEMENT_STATUS) != 0)
2185 fStatusGroup->SetVisible(true);
2186 fLoadingProgressBar->Show();
2187 } else {
2188 if (!fInterfaceVisible)
2189 fStatusGroup->SetVisible(false);
2190 // TODO: This is also used in _ShowInterface. Without it the status bar
2191 // doesn't always hide again. It may be an Interface Kit bug.
2192 while (!fLoadingProgressBar->IsHidden())
2193 fLoadingProgressBar->Hide();
2194 }
2195}
2196
2197
2198void
2199BrowserWindow::_InvokeButtonVisibly(BButton* button)
2200{
2201 button->SetValue(B_CONTROL_ON);
2202 UpdateIfNeeded();
2203 button->Invoke();
2204 snooze(1000);
2205 button->SetValue(B_CONTROL_OFF);
2206}
2207
2208
2209BString
2210BrowserWindow::_NewTabURL(bool isNewWindow) const
2211{
2212 BString url;
2213 uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy;
2214 // Implement new page policy
2215 switch (policy) {
2216 case OpenStartPage:
2217 url = fStartPageURL;
2218 break;
2219 case OpenSearchPage:
2220 url = fSearchPageURL;
2221 break;
2222 case CloneCurrentPage:
2223 if (CurrentWebView() != NULL__null)
2224 url = CurrentWebView()->MainFrameURL();
2225 break;
2226 case OpenBlankPage:
2227 default:
2228 break;
2229 }
2230 return url;
2231}
2232
2233BString
2234BrowserWindow::_EncodeURIComponent(const BString& search)
2235{
2236 const BString escCharList = " $&`:<>[]{}\"+#%@/;=?\\^|~\',";
2237 BString result = search;
2238 char hexcode[4];
2239
2240 for (int32 i = 0; i < result.Length(); i++) {
2241 if (escCharList.FindFirst(result[i]) != B_ERROR(-1)) {
2242 sprintf(hexcode, "%02X", (unsigned int)result[i]);
2243 result[i] = '%';
2244 result.Insert(hexcode, i + 1);
2245 i += 2;
2246 }
2247 }
2248
2249 return result;
2250}
2251
2252
2253void
2254BrowserWindow::_VisitURL(const BString& url)
2255{
2256 //fURLInputGroup->TextView()->SetText(url);
2257 CurrentWebView()->LoadURL(url.String());
2258}
2259
2260
2261void
2262BrowserWindow::_VisitSearchEngine(const BString& search)
2263{
2264 // TODO: Google Code-In Task to make default search
2265 // engine modifiable from Settings? :)
2266
2267 BString engine = "http://www.google.com/search?q=";
2268 engine += _EncodeURIComponent(search);
2269 // We have to take care of some of the escaping before
2270 // we hand over the string to WebKit, if we want queries
2271 // like "4+3" to not be searched as "4 3".
2272
2273 _VisitURL(engine);
2274}
2275
2276
2277inline bool
2278BrowserWindow::_IsValidDomainChar(char ch)
2279{
2280 // TODO: Currenlty, only a whitespace character
2281 // breaks a domain name. It might be
2282 // a good idea (or a bad one) to make
2283 // character filtering based on the
2284 // IDNA 2008 standard.
2285
2286 return ch != ' ';
2287}
2288
2289
2290void
2291BrowserWindow::_SmartURLHandler(const BString& url)
2292{
2293 // Only process if this doesn't look like a full URL (http:// or
2294 // file://, etc.)
2295
2296 BString temp;
2297 int32 at = url.FindFirst(":");
2298
2299 if (at != B_ERROR(-1)) {
2300 BString proto;
2301 url.CopyInto(proto, 0, at);
2302
2303 if (proto == "http" || proto == "https" || proto == "file")
2304 _VisitURL(url);
2305 else {
2306 temp = "application/x-vnd.Be.URL.";
2307 temp += proto;
2308
2309 char* argv[1] = { (char*)url.String() };
2310
2311 if (be_roster->Launch(temp.String(), 1, argv) != B_OK((int)0))
2312 _VisitSearchEngine(url);
2313 }
2314 } else if (url == "localhost")
2315 _VisitURL("http://localhost/");
2316 else {
2317 const char* localhostPrefix = "localhost/";
2318
2319 if(url.Compare(localhostPrefix, strlen(localhostPrefix)) == 0)
2320 _VisitURL(url);
2321 else {
2322 bool isURL = false;
2323
2324 for (int32 i = 0; i < url.CountChars(); i++) {
2325 if (url[i] == '.')
2326 isURL = true;
2327 else if (url[i] == '/')
2328 break;
2329 else if (!_IsValidDomainChar(url[i])) {
2330 isURL = false;
2331
2332 break;
2333 }
2334 }
2335
2336 if (isURL)
2337 _VisitURL(url);
2338 else
2339 _VisitSearchEngine(url);
2340 }
2341 }
2342}
2343
2344
2345void
2346BrowserWindow::_HandlePageSourceResult(const BMessage* message)
2347{
2348 // TODO: This should be done in an extra thread perhaps. Doing it in
2349 // the application thread is not much better, since it actually draws
2350 // the pages...
2351
2352 BPath pathToPageSource;
2353
2354 BString url;
2355 status_t ret = message->FindString("url", &url);
2356 if (ret == B_OK((int)0) && url.FindFirst("file://") == 0) {
2357 // Local file
2358 url.Remove(0, strlen("file://"));
2359 pathToPageSource.SetTo(url.String());
2360 } else {
2361 // Something else, store it.
2362 // TODO: What if it isn't HTML, but for example SVG?
2363 BString source;
2364 ret = message->FindString("source", &source);
2365
2366 if (ret == B_OK((int)0))
2367 ret = find_directory(B_COMMON_TEMP_DIRECTORY, &pathToPageSource);
2368
2369 BString tmpFileName("PageSource_");
2370 tmpFileName << system_time() << ".html";
2371 if (ret == B_OK((int)0))
2372 ret = pathToPageSource.Append(tmpFileName.String());
2373
2374 BFile pageSourceFile(pathToPageSource.Path(),
2375 B_CREATE_FILE0x0200 | B_ERASE_FILE0x0400 | B_WRITE_ONLY0x0001);
2376 if (ret == B_OK((int)0))
2377 ret = pageSourceFile.InitCheck();
2378
2379 if (ret == B_OK((int)0)) {
2380 ssize_t written = pageSourceFile.Write(source.String(),
2381 source.Length());
2382 if (written != source.Length())
2383 ret = (status_t)written;
2384 }
2385
2386 if (ret == B_OK((int)0)) {
2387 const char* type = "text/html";
2388 size_t size = strlen(type);
2389 pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size);
2390 // If it fails we don't care.
2391 }
2392 }
2393
2394 entry_ref ref;
2395 if (ret == B_OK((int)0))
2396 ret = get_ref_for_path(pathToPageSource.Path(), &ref);
2397
2398 if (ret == B_OK((int)0)) {
2399 BMessage refsMessage(B_REFS_RECEIVED);
2400 ret = refsMessage.AddRef("refs", &ref);
2401 if (ret == B_OK((int)0)) {
2402 ret = be_roster->Launch("text/x-source-code", &refsMessage);
2403 if (ret == B_ALREADY_RUNNING(((-2147483647 - 1) + 0x2000) + 4))
2404 ret = B_OK((int)0);
2405 }
2406 }
2407
2408 if (ret != B_OK((int)0)) {
2409 char buffer[1024];
2410 snprintf(buffer, sizeof(buffer), "Failed to show the "
2411 "page source: %s\n", strerror(ret));
2412 BAlert* alert = new BAlert(B_TRANSLATE("Page source error")BLocaleRoster::Default()->GetCatalog()->GetString(("Page source error"
), "WebPositive Window")
, buffer,
2413 B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK"
), "WebPositive Window")
);
2414 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2415 alert->Go(NULL__null);
2416 }
2417}